Projectile.cpp 72 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653
  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. idProjectile
  26. ===============================================================================
  27. */
  28. static const int BFG_DAMAGE_FREQUENCY = 333;
  29. static const float BOUNCE_SOUND_MIN_VELOCITY = 200.0f;
  30. static const float BOUNCE_SOUND_MAX_VELOCITY = 400.0f;
  31. const idEventDef EV_Explode( "<explode>", NULL );
  32. const idEventDef EV_Fizzle( "<fizzle>", NULL );
  33. const idEventDef EV_RadiusDamage( "<radiusdmg>", "e" );
  34. const idEventDef EV_GetProjectileState( "getProjectileState", NULL, 'd' );
  35. #ifdef _D3XP
  36. const idEventDef EV_CreateProjectile( "projectileCreateProjectile", "evv" );
  37. const idEventDef EV_LaunchProjectile( "projectileLaunchProjectile", "vvv" );
  38. const idEventDef EV_SetGravity( "setGravity", "f" );
  39. #endif
  40. CLASS_DECLARATION( idEntity, idProjectile )
  41. EVENT( EV_Explode, idProjectile::Event_Explode )
  42. EVENT( EV_Fizzle, idProjectile::Event_Fizzle )
  43. EVENT( EV_Touch, idProjectile::Event_Touch )
  44. EVENT( EV_RadiusDamage, idProjectile::Event_RadiusDamage )
  45. EVENT( EV_GetProjectileState, idProjectile::Event_GetProjectileState )
  46. #ifdef _D3XP
  47. EVENT( EV_CreateProjectile, idProjectile::Event_CreateProjectile )
  48. EVENT( EV_LaunchProjectile, idProjectile::Event_LaunchProjectile )
  49. EVENT( EV_SetGravity, idProjectile::Event_SetGravity )
  50. #endif
  51. END_CLASS
  52. /*
  53. ================
  54. idProjectile::idProjectile
  55. ================
  56. */
  57. idProjectile::idProjectile( void ) {
  58. owner = NULL;
  59. lightDefHandle = -1;
  60. thrust = 0.0f;
  61. thrust_end = 0;
  62. smokeFly = NULL;
  63. smokeFlyTime = 0;
  64. state = SPAWNED;
  65. lightOffset = vec3_zero;
  66. lightStartTime = 0;
  67. lightEndTime = 0;
  68. lightColor = vec3_zero;
  69. state = SPAWNED;
  70. damagePower = 1.0f;
  71. memset( &projectileFlags, 0, sizeof( projectileFlags ) );
  72. memset( &renderLight, 0, sizeof( renderLight ) );
  73. // note: for net_instanthit projectiles, we will force this back to false at spawn time
  74. fl.networkSync = true;
  75. netSyncPhysics = false;
  76. }
  77. /*
  78. ================
  79. idProjectile::Spawn
  80. ================
  81. */
  82. void idProjectile::Spawn( void ) {
  83. physicsObj.SetSelf( this );
  84. physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  85. physicsObj.SetContents( 0 );
  86. physicsObj.SetClipMask( 0 );
  87. physicsObj.PutToRest();
  88. SetPhysics( &physicsObj );
  89. }
  90. /*
  91. ================
  92. idProjectile::Save
  93. ================
  94. */
  95. void idProjectile::Save( idSaveGame *savefile ) const {
  96. owner.Save( savefile );
  97. projectileFlags_s flags = projectileFlags;
  98. LittleBitField( &flags, sizeof( flags ) );
  99. savefile->Write( &flags, sizeof( flags ) );
  100. savefile->WriteFloat( thrust );
  101. savefile->WriteInt( thrust_end );
  102. savefile->WriteRenderLight( renderLight );
  103. savefile->WriteInt( (int)lightDefHandle );
  104. savefile->WriteVec3( lightOffset );
  105. savefile->WriteInt( lightStartTime );
  106. savefile->WriteInt( lightEndTime );
  107. savefile->WriteVec3( lightColor );
  108. savefile->WriteParticle( smokeFly );
  109. savefile->WriteInt( smokeFlyTime );
  110. #ifdef _D3XP
  111. savefile->WriteInt( originalTimeGroup );
  112. #endif
  113. savefile->WriteInt( (int)state );
  114. savefile->WriteFloat( damagePower );
  115. savefile->WriteStaticObject( physicsObj );
  116. savefile->WriteStaticObject( thruster );
  117. }
  118. /*
  119. ================
  120. idProjectile::Restore
  121. ================
  122. */
  123. void idProjectile::Restore( idRestoreGame *savefile ) {
  124. owner.Restore( savefile );
  125. savefile->Read( &projectileFlags, sizeof( projectileFlags ) );
  126. LittleBitField( &projectileFlags, sizeof( projectileFlags ) );
  127. savefile->ReadFloat( thrust );
  128. savefile->ReadInt( thrust_end );
  129. savefile->ReadRenderLight( renderLight );
  130. savefile->ReadInt( (int &)lightDefHandle );
  131. savefile->ReadVec3( lightOffset );
  132. savefile->ReadInt( lightStartTime );
  133. savefile->ReadInt( lightEndTime );
  134. savefile->ReadVec3( lightColor );
  135. savefile->ReadParticle( smokeFly );
  136. savefile->ReadInt( smokeFlyTime );
  137. #ifdef _D3XP
  138. savefile->ReadInt( originalTimeGroup );
  139. #endif
  140. savefile->ReadInt( (int &)state );
  141. savefile->ReadFloat( damagePower );
  142. savefile->ReadStaticObject( physicsObj );
  143. RestorePhysics( &physicsObj );
  144. savefile->ReadStaticObject( thruster );
  145. thruster.SetPhysics( &physicsObj );
  146. if ( smokeFly != NULL ) {
  147. idVec3 dir;
  148. dir = physicsObj.GetLinearVelocity();
  149. dir.NormalizeFast();
  150. gameLocal.smokeParticles->EmitSmoke( smokeFly, gameLocal.time, gameLocal.random.RandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ );
  151. }
  152. #ifdef _D3XP
  153. if ( lightDefHandle >= 0 ) {
  154. lightDefHandle = gameRenderWorld->AddLightDef( &renderLight );
  155. }
  156. #endif
  157. }
  158. /*
  159. ================
  160. idProjectile::GetOwner
  161. ================
  162. */
  163. idEntity *idProjectile::GetOwner( void ) const {
  164. return owner.GetEntity();
  165. }
  166. /*
  167. ================
  168. idProjectile::Create
  169. ================
  170. */
  171. void idProjectile::Create( idEntity *owner, const idVec3 &start, const idVec3 &dir ) {
  172. idDict args;
  173. idStr shaderName;
  174. idVec3 light_color;
  175. idVec3 light_offset;
  176. idVec3 tmp;
  177. idMat3 axis;
  178. Unbind();
  179. // align z-axis of model with the direction
  180. axis = dir.ToMat3();
  181. tmp = axis[2];
  182. axis[2] = axis[0];
  183. axis[0] = -tmp;
  184. physicsObj.SetOrigin( start );
  185. physicsObj.SetAxis( axis );
  186. physicsObj.GetClipModel()->SetOwner( owner );
  187. this->owner = owner;
  188. memset( &renderLight, 0, sizeof( renderLight ) );
  189. shaderName = spawnArgs.GetString( "mtr_light_shader" );
  190. if ( *(const char *)shaderName ) {
  191. renderLight.shader = declManager->FindMaterial( shaderName, false );
  192. renderLight.pointLight = true;
  193. renderLight.lightRadius[0] =
  194. renderLight.lightRadius[1] =
  195. renderLight.lightRadius[2] = spawnArgs.GetFloat( "light_radius" );
  196. spawnArgs.GetVector( "light_color", "1 1 1", light_color );
  197. renderLight.shaderParms[0] = light_color[0];
  198. renderLight.shaderParms[1] = light_color[1];
  199. renderLight.shaderParms[2] = light_color[2];
  200. renderLight.shaderParms[3] = 1.0f;
  201. }
  202. spawnArgs.GetVector( "light_offset", "0 0 0", lightOffset );
  203. lightStartTime = 0;
  204. lightEndTime = 0;
  205. smokeFlyTime = 0;
  206. damagePower = 1.0f;
  207. #ifdef _D3XP
  208. if(spawnArgs.GetBool("reset_time_offset", "0")) {
  209. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  210. }
  211. #endif
  212. UpdateVisuals();
  213. state = CREATED;
  214. if ( spawnArgs.GetBool( "net_fullphysics" ) ) {
  215. netSyncPhysics = true;
  216. }
  217. }
  218. /*
  219. =================
  220. idProjectile::~idProjectile
  221. =================
  222. */
  223. idProjectile::~idProjectile() {
  224. StopSound( SND_CHANNEL_ANY, false );
  225. FreeLightDef();
  226. }
  227. /*
  228. =================
  229. idProjectile::FreeLightDef
  230. =================
  231. */
  232. void idProjectile::FreeLightDef( void ) {
  233. if ( lightDefHandle != -1 ) {
  234. gameRenderWorld->FreeLightDef( lightDefHandle );
  235. lightDefHandle = -1;
  236. }
  237. }
  238. /*
  239. =================
  240. idProjectile::Launch
  241. =================
  242. */
  243. void idProjectile::Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire, const float launchPower, const float dmgPower ) {
  244. float fuse;
  245. float startthrust;
  246. float endthrust;
  247. idVec3 velocity;
  248. idAngles angular_velocity;
  249. float linear_friction;
  250. float angular_friction;
  251. float contact_friction;
  252. float bounce;
  253. float mass;
  254. float speed;
  255. float gravity;
  256. idVec3 gravVec;
  257. idVec3 tmp;
  258. idMat3 axis;
  259. int thrust_start;
  260. int contents;
  261. int clipMask;
  262. // allow characters to throw projectiles during cinematics, but not the player
  263. if ( owner.GetEntity() && !owner.GetEntity()->IsType( idPlayer::Type ) ) {
  264. cinematic = owner.GetEntity()->cinematic;
  265. } else {
  266. cinematic = false;
  267. }
  268. thrust = spawnArgs.GetFloat( "thrust" );
  269. startthrust = spawnArgs.GetFloat( "thrust_start" );
  270. endthrust = spawnArgs.GetFloat( "thrust_end" );
  271. spawnArgs.GetVector( "velocity", "0 0 0", velocity );
  272. speed = velocity.Length() * launchPower;
  273. damagePower = dmgPower;
  274. spawnArgs.GetAngles( "angular_velocity", "0 0 0", angular_velocity );
  275. linear_friction = spawnArgs.GetFloat( "linear_friction" );
  276. angular_friction = spawnArgs.GetFloat( "angular_friction" );
  277. contact_friction = spawnArgs.GetFloat( "contact_friction" );
  278. bounce = spawnArgs.GetFloat( "bounce" );
  279. mass = spawnArgs.GetFloat( "mass" );
  280. gravity = spawnArgs.GetFloat( "gravity" );
  281. fuse = spawnArgs.GetFloat( "fuse" );
  282. projectileFlags.detonate_on_world = spawnArgs.GetBool( "detonate_on_world" );
  283. projectileFlags.detonate_on_actor = spawnArgs.GetBool( "detonate_on_actor" );
  284. projectileFlags.randomShaderSpin = spawnArgs.GetBool( "random_shader_spin" );
  285. if ( mass <= 0 ) {
  286. gameLocal.Error( "Invalid mass on '%s'\n", GetEntityDefName() );
  287. }
  288. thrust *= mass;
  289. thrust_start = SEC2MS( startthrust ) + gameLocal.time;
  290. thrust_end = SEC2MS( endthrust ) + gameLocal.time;
  291. lightStartTime = 0;
  292. lightEndTime = 0;
  293. if ( health ) {
  294. fl.takedamage = true;
  295. }
  296. gravVec = gameLocal.GetGravity();
  297. gravVec.NormalizeFast();
  298. Unbind();
  299. // align z-axis of model with the direction
  300. axis = dir.ToMat3();
  301. tmp = axis[2];
  302. axis[2] = axis[0];
  303. axis[0] = -tmp;
  304. contents = 0;
  305. clipMask = MASK_SHOT_RENDERMODEL;
  306. if ( spawnArgs.GetBool( "detonate_on_trigger" ) ) {
  307. contents |= CONTENTS_TRIGGER;
  308. }
  309. if ( !spawnArgs.GetBool( "no_contents" ) ) {
  310. contents |= CONTENTS_PROJECTILE;
  311. clipMask |= CONTENTS_PROJECTILE;
  312. }
  313. #ifdef _D3XP
  314. if ( !idStr::Cmp( this->GetEntityDefName(), "projectile_helltime_killer" ) ) {
  315. contents = CONTENTS_MOVEABLECLIP;
  316. clipMask = CONTENTS_MOVEABLECLIP;
  317. }
  318. #endif
  319. // don't do tracers on client, we don't know origin and direction
  320. if ( spawnArgs.GetBool( "tracers" ) && gameLocal.random.RandomFloat() > 0.5f ) {
  321. SetModel( spawnArgs.GetString( "model_tracer" ) );
  322. projectileFlags.isTracer = true;
  323. }
  324. physicsObj.SetMass( mass );
  325. physicsObj.SetFriction( linear_friction, angular_friction, contact_friction );
  326. if ( contact_friction == 0.0f ) {
  327. physicsObj.NoContact();
  328. }
  329. physicsObj.SetBouncyness( bounce );
  330. physicsObj.SetGravity( gravVec * gravity );
  331. physicsObj.SetContents( contents );
  332. physicsObj.SetClipMask( clipMask );
  333. physicsObj.SetLinearVelocity( axis[ 2 ] * speed + pushVelocity );
  334. physicsObj.SetAngularVelocity( angular_velocity.ToAngularVelocity() * axis );
  335. physicsObj.SetOrigin( start );
  336. physicsObj.SetAxis( axis );
  337. thruster.SetPosition( &physicsObj, 0, idVec3( GetPhysics()->GetBounds()[ 0 ].x, 0, 0 ) );
  338. if ( !gameLocal.isClient ) {
  339. if ( fuse <= 0 ) {
  340. // run physics for 1 second
  341. RunPhysics();
  342. PostEventMS( &EV_Remove, spawnArgs.GetInt( "remove_time", "1500" ) );
  343. } else if ( spawnArgs.GetBool( "detonate_on_fuse" ) ) {
  344. fuse -= timeSinceFire;
  345. if ( fuse < 0.0f ) {
  346. fuse = 0.0f;
  347. }
  348. PostEventSec( &EV_Explode, fuse );
  349. } else {
  350. fuse -= timeSinceFire;
  351. if ( fuse < 0.0f ) {
  352. fuse = 0.0f;
  353. }
  354. PostEventSec( &EV_Fizzle, fuse );
  355. }
  356. }
  357. if ( projectileFlags.isTracer ) {
  358. StartSound( "snd_tracer", SND_CHANNEL_BODY, 0, false, NULL );
  359. } else {
  360. StartSound( "snd_fly", SND_CHANNEL_BODY, 0, false, NULL );
  361. }
  362. smokeFlyTime = 0;
  363. const char *smokeName = spawnArgs.GetString( "smoke_fly" );
  364. if ( *smokeName != '\0' ) {
  365. smokeFly = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  366. smokeFlyTime = gameLocal.time;
  367. }
  368. #ifdef _D3XP
  369. originalTimeGroup = timeGroup;
  370. #endif
  371. // used for the plasma bolts but may have other uses as well
  372. if ( projectileFlags.randomShaderSpin ) {
  373. float f = gameLocal.random.RandomFloat();
  374. f *= 0.5f;
  375. renderEntity.shaderParms[SHADERPARM_DIVERSITY] = f;
  376. }
  377. UpdateVisuals();
  378. state = LAUNCHED;
  379. }
  380. /*
  381. ================
  382. idProjectile::Think
  383. ================
  384. */
  385. void idProjectile::Think( void ) {
  386. if ( thinkFlags & TH_THINK ) {
  387. if ( thrust && ( gameLocal.time < thrust_end ) ) {
  388. // evaluate force
  389. thruster.SetForce( GetPhysics()->GetAxis()[ 0 ] * thrust );
  390. thruster.Evaluate( gameLocal.time );
  391. }
  392. }
  393. // run physics
  394. RunPhysics();
  395. Present();
  396. // add the particles
  397. if ( smokeFly != NULL && smokeFlyTime && !IsHidden() ) {
  398. idVec3 dir = -GetPhysics()->GetLinearVelocity();
  399. dir.Normalize();
  400. #ifdef _D3XP
  401. SetTimeState ts(originalTimeGroup);
  402. #endif
  403. if ( !gameLocal.smokeParticles->EmitSmoke( smokeFly, smokeFlyTime, gameLocal.random.RandomFloat(), GetPhysics()->GetOrigin(), dir.ToMat3(), timeGroup /*_D3XP*/ ) ) {
  404. smokeFlyTime = gameLocal.time;
  405. }
  406. }
  407. // add the light
  408. if ( renderLight.lightRadius.x > 0.0f && g_projectileLights.GetBool() ) {
  409. renderLight.origin = GetPhysics()->GetOrigin() + GetPhysics()->GetAxis() * lightOffset;
  410. renderLight.axis = GetPhysics()->GetAxis();
  411. if ( ( lightDefHandle != -1 ) ) {
  412. if ( lightEndTime > 0 && gameLocal.time <= lightEndTime + gameLocal.GetMSec() ) {
  413. idVec3 color( 0, 0, 0 );
  414. if ( gameLocal.time < lightEndTime ) {
  415. float frac = ( float )( gameLocal.time - lightStartTime ) / ( float )( lightEndTime - lightStartTime );
  416. color.Lerp( lightColor, color, frac );
  417. }
  418. renderLight.shaderParms[SHADERPARM_RED] = color.x;
  419. renderLight.shaderParms[SHADERPARM_GREEN] = color.y;
  420. renderLight.shaderParms[SHADERPARM_BLUE] = color.z;
  421. }
  422. gameRenderWorld->UpdateLightDef( lightDefHandle, &renderLight );
  423. } else {
  424. lightDefHandle = gameRenderWorld->AddLightDef( &renderLight );
  425. }
  426. }
  427. }
  428. /*
  429. =================
  430. idProjectile::Collide
  431. =================
  432. */
  433. bool idProjectile::Collide( const trace_t &collision, const idVec3 &velocity ) {
  434. idEntity *ent;
  435. idEntity *ignore;
  436. const char *damageDefName;
  437. idVec3 dir;
  438. float push;
  439. float damageScale;
  440. if ( state == EXPLODED || state == FIZZLED ) {
  441. return true;
  442. }
  443. // predict the explosion
  444. if ( gameLocal.isClient ) {
  445. if ( ClientPredictionCollide( this, spawnArgs, collision, velocity, !spawnArgs.GetBool( "net_instanthit" ) ) ) {
  446. Explode( collision, NULL );
  447. return true;
  448. }
  449. return false;
  450. }
  451. // remove projectile when a 'noimpact' surface is hit
  452. if ( ( collision.c.material != NULL ) && ( collision.c.material->GetSurfaceFlags() & SURF_NOIMPACT ) ) {
  453. PostEventMS( &EV_Remove, 0 );
  454. common->DPrintf( "Projectile collision no impact\n" );
  455. return true;
  456. }
  457. // get the entity the projectile collided with
  458. ent = gameLocal.entities[ collision.c.entityNum ];
  459. if ( ent == owner.GetEntity() ) {
  460. assert( 0 );
  461. return true;
  462. }
  463. // just get rid of the projectile when it hits a player in noclip
  464. if ( ent->IsType( idPlayer::Type ) && static_cast<idPlayer *>( ent )->noclip ) {
  465. PostEventMS( &EV_Remove, 0 );
  466. return true;
  467. }
  468. // direction of projectile
  469. dir = velocity;
  470. dir.Normalize();
  471. // projectiles can apply an additional impulse next to the rigid body physics impulse
  472. if ( spawnArgs.GetFloat( "push", "0", push ) && push > 0.0f ) {
  473. ent->ApplyImpulse( this, collision.c.id, collision.c.point, push * dir );
  474. }
  475. // MP: projectiles open doors
  476. if ( gameLocal.isMultiplayer && ent->IsType( idDoor::Type ) && !static_cast< idDoor * >(ent)->IsOpen() && !ent->spawnArgs.GetBool( "no_touch" ) ) {
  477. ent->ProcessEvent( &EV_Activate , this );
  478. }
  479. if ( ent->IsType( idActor::Type ) || ( ent->IsType( idAFAttachment::Type ) && static_cast<const idAFAttachment*>(ent)->GetBody()->IsType( idActor::Type ) ) ) {
  480. if ( !projectileFlags.detonate_on_actor ) {
  481. return false;
  482. }
  483. } else {
  484. if ( !projectileFlags.detonate_on_world ) {
  485. if ( !StartSound( "snd_ricochet", SND_CHANNEL_ITEM, 0, true, NULL ) ) {
  486. float len = velocity.Length();
  487. if ( len > BOUNCE_SOUND_MIN_VELOCITY ) {
  488. SetSoundVolume( len > BOUNCE_SOUND_MAX_VELOCITY ? 1.0f : idMath::Sqrt( len - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / idMath::Sqrt( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) ) );
  489. StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, true, NULL );
  490. }
  491. }
  492. return false;
  493. }
  494. }
  495. SetOrigin( collision.endpos );
  496. SetAxis( collision.endAxis );
  497. // unlink the clip model because we no longer need it
  498. GetPhysics()->UnlinkClip();
  499. damageDefName = spawnArgs.GetString( "def_damage" );
  500. ignore = NULL;
  501. // if the hit entity takes damage
  502. if ( ent->fl.takedamage ) {
  503. if ( damagePower ) {
  504. damageScale = damagePower;
  505. } else {
  506. damageScale = 1.0f;
  507. }
  508. // if the projectile owner is a player
  509. if ( owner.GetEntity() && owner.GetEntity()->IsType( idPlayer::Type ) ) {
  510. // if the projectile hit an actor
  511. if ( ent->IsType( idActor::Type ) ) {
  512. idPlayer *player = static_cast<idPlayer *>( owner.GetEntity() );
  513. player->AddProjectileHits( 1 );
  514. damageScale *= player->PowerUpModifier( PROJECTILE_DAMAGE );
  515. }
  516. }
  517. if ( damageDefName[0] != '\0' ) {
  518. ent->Damage( this, owner.GetEntity(), dir, damageDefName, damageScale, CLIPMODEL_ID_TO_JOINT_HANDLE( collision.c.id ) );
  519. ignore = ent;
  520. }
  521. }
  522. // if the projectile causes a damage effect
  523. if ( spawnArgs.GetBool( "impact_damage_effect" ) ) {
  524. // if the hit entity has a special damage effect
  525. if ( ent->spawnArgs.GetBool( "bleed" ) ) {
  526. ent->AddDamageEffect( collision, velocity, damageDefName );
  527. } else {
  528. AddDefaultDamageEffect( collision, velocity );
  529. }
  530. }
  531. Explode( collision, ignore );
  532. return true;
  533. }
  534. /*
  535. =================
  536. idProjectile::DefaultDamageEffect
  537. =================
  538. */
  539. void idProjectile::DefaultDamageEffect( idEntity *soundEnt, const idDict &projectileDef, const trace_t &collision, const idVec3 &velocity ) {
  540. const char *decal, *sound, *typeName;
  541. surfTypes_t materialType;
  542. if ( collision.c.material != NULL ) {
  543. materialType = collision.c.material->GetSurfaceType();
  544. } else {
  545. materialType = SURFTYPE_METAL;
  546. }
  547. // get material type name
  548. typeName = gameLocal.sufaceTypeNames[ materialType ];
  549. // play impact sound
  550. sound = projectileDef.GetString( va( "snd_%s", typeName ) );
  551. if ( *sound == '\0' ) {
  552. sound = projectileDef.GetString( "snd_metal" );
  553. }
  554. if ( *sound == '\0' ) {
  555. sound = projectileDef.GetString( "snd_impact" );
  556. }
  557. if ( *sound != '\0' ) {
  558. soundEnt->StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
  559. }
  560. // project decal
  561. decal = projectileDef.GetString( va( "mtr_detonate_%s", typeName ) );
  562. if ( *decal == '\0' ) {
  563. decal = projectileDef.GetString( "mtr_detonate" );
  564. }
  565. if ( *decal != '\0' ) {
  566. gameLocal.ProjectDecal( collision.c.point, -collision.c.normal, 8.0f, true, projectileDef.GetFloat( "decal_size", "6.0" ), decal );
  567. }
  568. }
  569. /*
  570. =================
  571. idProjectile::AddDefaultDamageEffect
  572. =================
  573. */
  574. void idProjectile::AddDefaultDamageEffect( const trace_t &collision, const idVec3 &velocity ) {
  575. DefaultDamageEffect( this, spawnArgs, collision, velocity );
  576. if ( gameLocal.isServer && fl.networkSync ) {
  577. idBitMsg msg;
  578. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  579. int excludeClient;
  580. if ( spawnArgs.GetBool( "net_instanthit" ) ) {
  581. excludeClient = owner.GetEntityNum();
  582. } else {
  583. excludeClient = -1;
  584. }
  585. msg.Init( msgBuf, sizeof( msgBuf ) );
  586. msg.BeginWriting();
  587. msg.WriteFloat( collision.c.point[0] );
  588. msg.WriteFloat( collision.c.point[1] );
  589. msg.WriteFloat( collision.c.point[2] );
  590. msg.WriteDir( collision.c.normal, 24 );
  591. msg.WriteLong( ( collision.c.material != NULL ) ? gameLocal.ServerRemapDecl( -1, DECL_MATERIAL, collision.c.material->Index() ) : -1 );
  592. msg.WriteFloat( velocity[0], 5, 10 );
  593. msg.WriteFloat( velocity[1], 5, 10 );
  594. msg.WriteFloat( velocity[2], 5, 10 );
  595. ServerSendEvent( EVENT_DAMAGE_EFFECT, &msg, false, excludeClient );
  596. }
  597. }
  598. /*
  599. ================
  600. idProjectile::Killed
  601. ================
  602. */
  603. void idProjectile::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  604. if ( spawnArgs.GetBool( "detonate_on_death" ) ) {
  605. trace_t collision;
  606. memset( &collision, 0, sizeof( collision ) );
  607. collision.endAxis = GetPhysics()->GetAxis();
  608. collision.endpos = GetPhysics()->GetOrigin();
  609. collision.c.point = GetPhysics()->GetOrigin();
  610. collision.c.normal.Set( 0, 0, 1 );
  611. Explode( collision, NULL );
  612. physicsObj.ClearContacts();
  613. physicsObj.PutToRest();
  614. } else {
  615. Fizzle();
  616. }
  617. }
  618. /*
  619. ================
  620. idProjectile::Fizzle
  621. ================
  622. */
  623. void idProjectile::Fizzle( void ) {
  624. if ( state == EXPLODED || state == FIZZLED ) {
  625. return;
  626. }
  627. StopSound( SND_CHANNEL_BODY, false );
  628. StartSound( "snd_fizzle", SND_CHANNEL_BODY, 0, false, NULL );
  629. // fizzle FX
  630. const char *psystem = spawnArgs.GetString( "smoke_fuse" );
  631. if ( psystem && *psystem ) {
  632. //FIXME:SMOKE gameLocal.particles->SpawnParticles( GetPhysics()->GetOrigin(), vec3_origin, psystem );
  633. }
  634. // we need to work out how long the effects last and then remove them at that time
  635. // for example, bullets have no real effects
  636. if ( smokeFly && smokeFlyTime ) {
  637. smokeFlyTime = 0;
  638. }
  639. fl.takedamage = false;
  640. physicsObj.SetContents( 0 );
  641. physicsObj.GetClipModel()->Unlink();
  642. physicsObj.PutToRest();
  643. Hide();
  644. FreeLightDef();
  645. state = FIZZLED;
  646. if ( gameLocal.isClient ) {
  647. return;
  648. }
  649. CancelEvents( &EV_Fizzle );
  650. PostEventMS( &EV_Remove, spawnArgs.GetInt( "remove_time", "1500" ) );
  651. }
  652. /*
  653. ================
  654. idProjectile::Event_RadiusDamage
  655. ================
  656. */
  657. void idProjectile::Event_RadiusDamage( idEntity *ignore ) {
  658. const char *splash_damage = spawnArgs.GetString( "def_splash_damage" );
  659. if ( splash_damage[0] != '\0' ) {
  660. gameLocal.RadiusDamage( physicsObj.GetOrigin(), this, owner.GetEntity(), ignore, this, splash_damage, damagePower );
  661. }
  662. }
  663. /*
  664. ================
  665. idProjectile::Event_RadiusDamage
  666. ================
  667. */
  668. void idProjectile::Event_GetProjectileState( void ) {
  669. idThread::ReturnInt( state );
  670. }
  671. /*
  672. ================
  673. idProjectile::Explode
  674. ================
  675. */
  676. void idProjectile::Explode( const trace_t &collision, idEntity *ignore ) {
  677. const char *fxname, *light_shader, *sndExplode;
  678. float light_fadetime;
  679. idVec3 normal;
  680. int removeTime;
  681. if ( state == EXPLODED || state == FIZZLED ) {
  682. return;
  683. }
  684. // stop sound
  685. StopSound( SND_CHANNEL_BODY2, false );
  686. // play explode sound
  687. switch ( ( int ) damagePower ) {
  688. case 2: sndExplode = "snd_explode2"; break;
  689. case 3: sndExplode = "snd_explode3"; break;
  690. case 4: sndExplode = "snd_explode4"; break;
  691. default: sndExplode = "snd_explode"; break;
  692. }
  693. StartSound( sndExplode, SND_CHANNEL_BODY, 0, true, NULL );
  694. // we need to work out how long the effects last and then remove them at that time
  695. // for example, bullets have no real effects
  696. if ( smokeFly && smokeFlyTime ) {
  697. smokeFlyTime = 0;
  698. }
  699. Hide();
  700. FreeLightDef();
  701. if ( spawnArgs.GetVector( "detonation_axis", "", normal ) ) {
  702. GetPhysics()->SetAxis( normal.ToMat3() );
  703. }
  704. GetPhysics()->SetOrigin( collision.endpos + 2.0f * collision.c.normal );
  705. // default remove time
  706. removeTime = spawnArgs.GetInt( "remove_time", "1500" );
  707. // change the model, usually to a PRT
  708. fxname = NULL;
  709. if ( g_testParticle.GetInteger() == TEST_PARTICLE_IMPACT ) {
  710. fxname = g_testParticleName.GetString();
  711. } else {
  712. fxname = spawnArgs.GetString( "model_detonate" );
  713. }
  714. int surfaceType = collision.c.material != NULL ? collision.c.material->GetSurfaceType() : SURFTYPE_METAL;
  715. if ( !( fxname && *fxname ) ) {
  716. if ( ( surfaceType == SURFTYPE_NONE ) || ( surfaceType == SURFTYPE_METAL ) || ( surfaceType == SURFTYPE_STONE ) ) {
  717. fxname = spawnArgs.GetString( "model_smokespark" );
  718. } else if ( surfaceType == SURFTYPE_RICOCHET ) {
  719. fxname = spawnArgs.GetString( "model_ricochet" );
  720. } else {
  721. fxname = spawnArgs.GetString( "model_smoke" );
  722. }
  723. }
  724. #ifdef _D3XP
  725. // If the explosion is in liquid, spawn a particle splash
  726. idVec3 testOrg = GetPhysics()->GetOrigin();
  727. int testC = gameLocal.clip.Contents( testOrg, NULL, mat3_identity, CONTENTS_WATER, this );
  728. if ( testC & CONTENTS_WATER ) {
  729. idFuncEmitter *splashEnt;
  730. idDict splashArgs;
  731. splashArgs.Set( "model", "sludgebulletimpact.prt" );
  732. splashArgs.Set( "start_off", "1" );
  733. splashEnt = static_cast<idFuncEmitter *>( gameLocal.SpawnEntityType( idFuncEmitter::Type, &splashArgs ) );
  734. splashEnt->GetPhysics()->SetOrigin( testOrg );
  735. splashEnt->PostEventMS( &EV_Activate, 0, this );
  736. splashEnt->PostEventMS( &EV_Remove, 1500 );
  737. // HACK - if this is a chaingun bullet, don't do the normal effect
  738. if ( !idStr::Cmp( spawnArgs.GetString( "def_damage" ), "damage_bullet_chaingun" ) ) {
  739. fxname = NULL;
  740. }
  741. }
  742. #endif
  743. if ( fxname && *fxname ) {
  744. SetModel( fxname );
  745. renderEntity.shaderParms[SHADERPARM_RED] =
  746. renderEntity.shaderParms[SHADERPARM_GREEN] =
  747. renderEntity.shaderParms[SHADERPARM_BLUE] =
  748. renderEntity.shaderParms[SHADERPARM_ALPHA] = 1.0f;
  749. renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
  750. renderEntity.shaderParms[SHADERPARM_DIVERSITY] = gameLocal.random.CRandomFloat();
  751. Show();
  752. removeTime = ( removeTime > 3000 ) ? removeTime : 3000;
  753. }
  754. // explosion light
  755. light_shader = spawnArgs.GetString( "mtr_explode_light_shader" );
  756. #ifdef CTF
  757. if ( gameLocal.mpGame.IsGametypeFlagBased() && gameLocal.serverInfo.GetBool("si_midnight") )
  758. {
  759. light_shader = "lights/midnight_grenade";
  760. }
  761. #endif
  762. if ( *light_shader ) {
  763. renderLight.shader = declManager->FindMaterial( light_shader, false );
  764. renderLight.pointLight = true;
  765. renderLight.lightRadius[0] =
  766. renderLight.lightRadius[1] =
  767. renderLight.lightRadius[2] = spawnArgs.GetFloat( "explode_light_radius" );
  768. #ifdef CTF
  769. // Midnight ctf
  770. if ( gameLocal.mpGame.IsGametypeFlagBased() && gameLocal.serverInfo.GetBool("si_midnight") )
  771. {
  772. renderLight.lightRadius[0] =
  773. renderLight.lightRadius[1] =
  774. renderLight.lightRadius[2] = spawnArgs.GetFloat( "explode_light_radius" ) * 2;
  775. }
  776. #endif
  777. spawnArgs.GetVector( "explode_light_color", "1 1 1", lightColor );
  778. renderLight.shaderParms[SHADERPARM_RED] = lightColor.x;
  779. renderLight.shaderParms[SHADERPARM_GREEN] = lightColor.y;
  780. renderLight.shaderParms[SHADERPARM_BLUE] = lightColor.z;
  781. renderLight.shaderParms[SHADERPARM_ALPHA] = 1.0f;
  782. renderLight.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
  783. #ifdef CTF
  784. // Midnight ctf
  785. if ( gameLocal.mpGame.IsGametypeFlagBased() && gameLocal.serverInfo.GetBool("si_midnight") )
  786. {
  787. light_fadetime = 3.0f;
  788. }
  789. else
  790. #endif
  791. light_fadetime = spawnArgs.GetFloat( "explode_light_fadetime", "0.5" );
  792. lightStartTime = gameLocal.time;
  793. lightEndTime = gameLocal.time + SEC2MS( light_fadetime );
  794. BecomeActive( TH_THINK );
  795. }
  796. fl.takedamage = false;
  797. physicsObj.SetContents( 0 );
  798. physicsObj.PutToRest();
  799. state = EXPLODED;
  800. if ( gameLocal.isClient ) {
  801. return;
  802. }
  803. // alert the ai
  804. gameLocal.AlertAI( owner.GetEntity() );
  805. // bind the projectile to the impact entity if necesary
  806. if ( gameLocal.entities[collision.c.entityNum] && spawnArgs.GetBool( "bindOnImpact" ) ) {
  807. Bind( gameLocal.entities[collision.c.entityNum], true );
  808. }
  809. // splash damage
  810. if ( !projectileFlags.noSplashDamage ) {
  811. float delay = spawnArgs.GetFloat( "delay_splash" );
  812. if ( delay ) {
  813. if ( removeTime < delay * 1000 ) {
  814. removeTime = ( delay + 0.10 ) * 1000;
  815. }
  816. PostEventSec( &EV_RadiusDamage, delay, ignore );
  817. } else {
  818. Event_RadiusDamage( ignore );
  819. }
  820. }
  821. // spawn debris entities
  822. int fxdebris = spawnArgs.GetInt( "debris_count" );
  823. if ( fxdebris ) {
  824. const idDict *debris = gameLocal.FindEntityDefDict( "projectile_debris", false );
  825. if ( debris ) {
  826. int amount = gameLocal.random.RandomInt( fxdebris );
  827. for ( int i = 0; i < amount; i++ ) {
  828. idEntity *ent;
  829. idVec3 dir;
  830. dir.x = gameLocal.random.CRandomFloat() * 4.0f;
  831. dir.y = gameLocal.random.CRandomFloat() * 4.0f;
  832. dir.z = gameLocal.random.RandomFloat() * 8.0f;
  833. dir.Normalize();
  834. gameLocal.SpawnEntityDef( *debris, &ent, false );
  835. if ( !ent || !ent->IsType( idDebris::Type ) ) {
  836. gameLocal.Error( "'projectile_debris' is not an idDebris" );
  837. }
  838. idDebris *debris = static_cast<idDebris *>(ent);
  839. debris->Create( owner.GetEntity(), physicsObj.GetOrigin(), dir.ToMat3() );
  840. debris->Launch();
  841. }
  842. }
  843. debris = gameLocal.FindEntityDefDict( "projectile_shrapnel", false );
  844. if ( debris ) {
  845. int amount = gameLocal.random.RandomInt( fxdebris );
  846. for ( int i = 0; i < amount; i++ ) {
  847. idEntity *ent;
  848. idVec3 dir;
  849. dir.x = gameLocal.random.CRandomFloat() * 8.0f;
  850. dir.y = gameLocal.random.CRandomFloat() * 8.0f;
  851. dir.z = gameLocal.random.RandomFloat() * 8.0f + 8.0f;
  852. dir.Normalize();
  853. gameLocal.SpawnEntityDef( *debris, &ent, false );
  854. if ( !ent || !ent->IsType( idDebris::Type ) ) {
  855. gameLocal.Error( "'projectile_shrapnel' is not an idDebris" );
  856. }
  857. idDebris *debris = static_cast<idDebris *>(ent);
  858. debris->Create( owner.GetEntity(), physicsObj.GetOrigin(), dir.ToMat3() );
  859. debris->Launch();
  860. }
  861. }
  862. }
  863. CancelEvents( &EV_Explode );
  864. PostEventMS( &EV_Remove, removeTime );
  865. }
  866. /*
  867. ================
  868. idProjectile::GetVelocity
  869. ================
  870. */
  871. idVec3 idProjectile::GetVelocity( const idDict *projectile ) {
  872. idVec3 velocity;
  873. projectile->GetVector( "velocity", "0 0 0", velocity );
  874. return velocity;
  875. }
  876. /*
  877. ================
  878. idProjectile::GetGravity
  879. ================
  880. */
  881. idVec3 idProjectile::GetGravity( const idDict *projectile ) {
  882. float gravity;
  883. gravity = projectile->GetFloat( "gravity" );
  884. return idVec3( 0, 0, -gravity );
  885. }
  886. /*
  887. ================
  888. idProjectile::Event_Explode
  889. ================
  890. */
  891. void idProjectile::Event_Explode( void ) {
  892. trace_t collision;
  893. memset( &collision, 0, sizeof( collision ) );
  894. collision.endAxis = GetPhysics()->GetAxis();
  895. collision.endpos = GetPhysics()->GetOrigin();
  896. collision.c.point = GetPhysics()->GetOrigin();
  897. collision.c.normal.Set( 0, 0, 1 );
  898. AddDefaultDamageEffect( collision, collision.c.normal );
  899. Explode( collision, NULL );
  900. }
  901. /*
  902. ================
  903. idProjectile::Event_Fizzle
  904. ================
  905. */
  906. void idProjectile::Event_Fizzle( void ) {
  907. Fizzle();
  908. }
  909. /*
  910. ================
  911. idProjectile::Event_Touch
  912. ================
  913. */
  914. void idProjectile::Event_Touch( idEntity *other, trace_t *trace ) {
  915. if ( IsHidden() ) {
  916. return;
  917. }
  918. #ifdef CTF
  919. // Projectiles do not collide with flags
  920. if ( other->IsType( idItemTeam::Type ) )
  921. return;
  922. #endif
  923. if ( other != owner.GetEntity() ) {
  924. trace_t collision;
  925. memset( &collision, 0, sizeof( collision ) );
  926. collision.endAxis = GetPhysics()->GetAxis();
  927. collision.endpos = GetPhysics()->GetOrigin();
  928. collision.c.point = GetPhysics()->GetOrigin();
  929. collision.c.normal.Set( 0, 0, 1 );
  930. AddDefaultDamageEffect( collision, collision.c.normal );
  931. Explode( collision, NULL );
  932. }
  933. }
  934. #ifdef _D3XP
  935. /*
  936. ================
  937. idProjectile::CatchProjectile
  938. ================
  939. */
  940. void idProjectile::CatchProjectile( idEntity* o, const char* reflectName ) {
  941. idEntity *prevowner = owner.GetEntity();
  942. owner = o;
  943. physicsObj.GetClipModel()->SetOwner( o );
  944. if ( this->IsType( idGuidedProjectile::Type ) ) {
  945. idGuidedProjectile *proj = static_cast<idGuidedProjectile*>(this);
  946. proj->SetEnemy( prevowner );
  947. }
  948. idStr s = spawnArgs.GetString( "def_damage" );
  949. s += reflectName;
  950. const idDict *damageDef = gameLocal.FindEntityDefDict( s, false );
  951. if ( damageDef ) {
  952. spawnArgs.Set( "def_damage", s );
  953. }
  954. }
  955. /*
  956. ================
  957. idProjectile::GetProjectileState
  958. ================
  959. */
  960. int idProjectile::GetProjectileState( void ) {
  961. return (int)state;
  962. }
  963. /*
  964. ================
  965. idProjectile::Event_CreateProjectile
  966. ================
  967. */
  968. void idProjectile::Event_CreateProjectile( idEntity *owner, const idVec3 &start, const idVec3 &dir ) {
  969. Create(owner, start, dir);
  970. }
  971. /*
  972. ================
  973. idProjectile::Event_LaunchProjectile
  974. ================
  975. */
  976. void idProjectile::Event_LaunchProjectile( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity ) {
  977. Launch(start, dir, pushVelocity);
  978. }
  979. /*
  980. ================
  981. idProjectile::Event_SetGravity
  982. ================
  983. */
  984. void idProjectile::Event_SetGravity( float gravity ) {
  985. idVec3 gravVec;
  986. gravVec = gameLocal.GetGravity();
  987. gravVec.NormalizeFast();
  988. physicsObj.SetGravity(gravVec * gravity);
  989. }
  990. #endif
  991. /*
  992. =================
  993. idProjectile::ClientPredictionCollide
  994. =================
  995. */
  996. bool idProjectile::ClientPredictionCollide( idEntity *soundEnt, const idDict &projectileDef, const trace_t &collision, const idVec3 &velocity, bool addDamageEffect ) {
  997. idEntity *ent;
  998. // remove projectile when a 'noimpact' surface is hit
  999. if ( collision.c.material && ( collision.c.material->GetSurfaceFlags() & SURF_NOIMPACT ) ) {
  1000. return false;
  1001. }
  1002. // get the entity the projectile collided with
  1003. ent = gameLocal.entities[ collision.c.entityNum ];
  1004. if ( ent == NULL ) {
  1005. return false;
  1006. }
  1007. // don't do anything if hitting a noclip player
  1008. if ( ent->IsType( idPlayer::Type ) && static_cast<idPlayer *>( ent )->noclip ) {
  1009. return false;
  1010. }
  1011. if ( ent->IsType( idActor::Type ) || ( ent->IsType( idAFAttachment::Type ) && static_cast<const idAFAttachment*>(ent)->GetBody()->IsType( idActor::Type ) ) ) {
  1012. if ( !projectileDef.GetBool( "detonate_on_actor" ) ) {
  1013. return false;
  1014. }
  1015. } else {
  1016. if ( !projectileDef.GetBool( "detonate_on_world" ) ) {
  1017. return false;
  1018. }
  1019. }
  1020. // if the projectile causes a damage effect
  1021. if ( addDamageEffect && projectileDef.GetBool( "impact_damage_effect" ) ) {
  1022. // if the hit entity does not have a special damage effect
  1023. if ( !ent->spawnArgs.GetBool( "bleed" ) ) {
  1024. // predict damage effect
  1025. DefaultDamageEffect( soundEnt, projectileDef, collision, velocity );
  1026. }
  1027. }
  1028. return true;
  1029. }
  1030. /*
  1031. ================
  1032. idProjectile::ClientPredictionThink
  1033. ================
  1034. */
  1035. void idProjectile::ClientPredictionThink( void ) {
  1036. if ( !renderEntity.hModel ) {
  1037. return;
  1038. }
  1039. Think();
  1040. }
  1041. /*
  1042. ================
  1043. idProjectile::WriteToSnapshot
  1044. ================
  1045. */
  1046. void idProjectile::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1047. msg.WriteBits( owner.GetSpawnId(), 32 );
  1048. msg.WriteBits( state, 3 );
  1049. msg.WriteBits( fl.hidden, 1 );
  1050. if ( netSyncPhysics ) {
  1051. msg.WriteBits( 1, 1 );
  1052. physicsObj.WriteToSnapshot( msg );
  1053. } else {
  1054. msg.WriteBits( 0, 1 );
  1055. const idVec3 &origin = physicsObj.GetOrigin();
  1056. const idVec3 &velocity = physicsObj.GetLinearVelocity();
  1057. msg.WriteFloat( origin.x );
  1058. msg.WriteFloat( origin.y );
  1059. msg.WriteFloat( origin.z );
  1060. msg.WriteDeltaFloat( 0.0f, velocity[0], RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
  1061. msg.WriteDeltaFloat( 0.0f, velocity[1], RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
  1062. msg.WriteDeltaFloat( 0.0f, velocity[2], RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
  1063. }
  1064. }
  1065. /*
  1066. ================
  1067. idProjectile::ReadFromSnapshot
  1068. ================
  1069. */
  1070. void idProjectile::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1071. projectileState_t newState;
  1072. owner.SetSpawnId( msg.ReadBits( 32 ) );
  1073. newState = (projectileState_t) msg.ReadBits( 3 );
  1074. if ( msg.ReadBits( 1 ) ) {
  1075. Hide();
  1076. } else {
  1077. Show();
  1078. }
  1079. while( state != newState ) {
  1080. switch( state ) {
  1081. case SPAWNED: {
  1082. Create( owner.GetEntity(), vec3_origin, idVec3( 1, 0, 0 ) );
  1083. break;
  1084. }
  1085. case CREATED: {
  1086. // the right origin and direction are required if you want bullet traces
  1087. Launch( vec3_origin, idVec3( 1, 0, 0 ), vec3_origin );
  1088. break;
  1089. }
  1090. case LAUNCHED: {
  1091. if ( newState == FIZZLED ) {
  1092. Fizzle();
  1093. } else {
  1094. trace_t collision;
  1095. memset( &collision, 0, sizeof( collision ) );
  1096. collision.endAxis = GetPhysics()->GetAxis();
  1097. collision.endpos = GetPhysics()->GetOrigin();
  1098. collision.c.point = GetPhysics()->GetOrigin();
  1099. collision.c.normal.Set( 0, 0, 1 );
  1100. Explode( collision, NULL );
  1101. }
  1102. break;
  1103. }
  1104. case FIZZLED:
  1105. case EXPLODED: {
  1106. StopSound( SND_CHANNEL_BODY2, false );
  1107. gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &renderEntity );
  1108. state = SPAWNED;
  1109. break;
  1110. }
  1111. }
  1112. }
  1113. if ( msg.ReadBits( 1 ) ) {
  1114. physicsObj.ReadFromSnapshot( msg );
  1115. } else {
  1116. idVec3 origin;
  1117. idVec3 velocity;
  1118. idVec3 tmp;
  1119. idMat3 axis;
  1120. origin.x = msg.ReadFloat();
  1121. origin.y = msg.ReadFloat();
  1122. origin.z = msg.ReadFloat();
  1123. velocity.x = msg.ReadDeltaFloat( 0.0f, RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
  1124. velocity.y = msg.ReadDeltaFloat( 0.0f, RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
  1125. velocity.z = msg.ReadDeltaFloat( 0.0f, RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
  1126. physicsObj.SetOrigin( origin );
  1127. physicsObj.SetLinearVelocity( velocity );
  1128. // align z-axis of model with the direction
  1129. velocity.NormalizeFast();
  1130. axis = velocity.ToMat3();
  1131. tmp = axis[2];
  1132. axis[2] = axis[0];
  1133. axis[0] = -tmp;
  1134. physicsObj.SetAxis( axis );
  1135. }
  1136. if ( msg.HasChanged() ) {
  1137. UpdateVisuals();
  1138. }
  1139. }
  1140. /*
  1141. ================
  1142. idProjectile::ClientReceiveEvent
  1143. ================
  1144. */
  1145. bool idProjectile::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  1146. trace_t collision;
  1147. idVec3 velocity;
  1148. switch( event ) {
  1149. case EVENT_DAMAGE_EFFECT: {
  1150. memset( &collision, 0, sizeof( collision ) );
  1151. collision.c.point[0] = msg.ReadFloat();
  1152. collision.c.point[1] = msg.ReadFloat();
  1153. collision.c.point[2] = msg.ReadFloat();
  1154. collision.c.normal = msg.ReadDir( 24 );
  1155. int index = gameLocal.ClientRemapDecl( DECL_MATERIAL, msg.ReadLong() );
  1156. collision.c.material = ( index != -1 ) ? static_cast<const idMaterial *>( declManager->DeclByIndex( DECL_MATERIAL, index ) ) : NULL;
  1157. velocity[0] = msg.ReadFloat( 5, 10 );
  1158. velocity[1] = msg.ReadFloat( 5, 10 );
  1159. velocity[2] = msg.ReadFloat( 5, 10 );
  1160. DefaultDamageEffect( this, spawnArgs, collision, velocity );
  1161. return true;
  1162. }
  1163. default: {
  1164. return idEntity::ClientReceiveEvent( event, time, msg );
  1165. }
  1166. }
  1167. return false;
  1168. }
  1169. /*
  1170. ===============================================================================
  1171. idGuidedProjectile
  1172. ===============================================================================
  1173. */
  1174. #ifdef _D3XP
  1175. const idEventDef EV_SetEnemy( "setEnemy", "E" );
  1176. #endif
  1177. CLASS_DECLARATION( idProjectile, idGuidedProjectile )
  1178. #ifdef _D3XP
  1179. EVENT( EV_SetEnemy, idGuidedProjectile::Event_SetEnemy )
  1180. #endif
  1181. END_CLASS
  1182. /*
  1183. ================
  1184. idGuidedProjectile::idGuidedProjectile
  1185. ================
  1186. */
  1187. idGuidedProjectile::idGuidedProjectile( void ) {
  1188. enemy = NULL;
  1189. speed = 0.0f;
  1190. turn_max = 0.0f;
  1191. clamp_dist = 0.0f;
  1192. rndScale = ang_zero;
  1193. rndAng = ang_zero;
  1194. rndUpdateTime = 0;
  1195. angles = ang_zero;
  1196. burstMode = false;
  1197. burstDist = 0;
  1198. burstVelocity = 0.0f;
  1199. unGuided = false;
  1200. }
  1201. /*
  1202. =================
  1203. idGuidedProjectile::~idGuidedProjectile
  1204. =================
  1205. */
  1206. idGuidedProjectile::~idGuidedProjectile( void ) {
  1207. }
  1208. /*
  1209. ================
  1210. idGuidedProjectile::Spawn
  1211. ================
  1212. */
  1213. void idGuidedProjectile::Spawn( void ) {
  1214. }
  1215. /*
  1216. ================
  1217. idGuidedProjectile::Save
  1218. ================
  1219. */
  1220. void idGuidedProjectile::Save( idSaveGame *savefile ) const {
  1221. enemy.Save( savefile );
  1222. savefile->WriteFloat( speed );
  1223. savefile->WriteAngles( rndScale );
  1224. savefile->WriteAngles( rndAng );
  1225. savefile->WriteInt( rndUpdateTime );
  1226. savefile->WriteFloat( turn_max );
  1227. savefile->WriteFloat( clamp_dist );
  1228. savefile->WriteAngles( angles );
  1229. savefile->WriteBool( burstMode );
  1230. savefile->WriteBool( unGuided );
  1231. savefile->WriteFloat( burstDist );
  1232. savefile->WriteFloat( burstVelocity );
  1233. }
  1234. /*
  1235. ================
  1236. idGuidedProjectile::Restore
  1237. ================
  1238. */
  1239. void idGuidedProjectile::Restore( idRestoreGame *savefile ) {
  1240. enemy.Restore( savefile );
  1241. savefile->ReadFloat( speed );
  1242. savefile->ReadAngles( rndScale );
  1243. savefile->ReadAngles( rndAng );
  1244. savefile->ReadInt( rndUpdateTime );
  1245. savefile->ReadFloat( turn_max );
  1246. savefile->ReadFloat( clamp_dist );
  1247. savefile->ReadAngles( angles );
  1248. savefile->ReadBool( burstMode );
  1249. savefile->ReadBool( unGuided );
  1250. savefile->ReadFloat( burstDist );
  1251. savefile->ReadFloat( burstVelocity );
  1252. }
  1253. /*
  1254. ================
  1255. idGuidedProjectile::GetSeekPos
  1256. ================
  1257. */
  1258. void idGuidedProjectile::GetSeekPos( idVec3 &out ) {
  1259. idEntity *enemyEnt = enemy.GetEntity();
  1260. if ( enemyEnt ) {
  1261. if ( enemyEnt->IsType( idActor::Type ) ) {
  1262. out = static_cast<idActor *>(enemyEnt)->GetEyePosition();
  1263. out.z -= 12.0f;
  1264. } else {
  1265. out = enemyEnt->GetPhysics()->GetOrigin();
  1266. }
  1267. } else {
  1268. out = GetPhysics()->GetOrigin() + physicsObj.GetLinearVelocity() * 2.0f;
  1269. }
  1270. }
  1271. /*
  1272. ================
  1273. idGuidedProjectile::Think
  1274. ================
  1275. */
  1276. void idGuidedProjectile::Think( void ) {
  1277. idVec3 dir;
  1278. idVec3 seekPos;
  1279. idVec3 velocity;
  1280. idVec3 nose;
  1281. idVec3 tmp;
  1282. idMat3 axis;
  1283. idAngles dirAng;
  1284. idAngles diff;
  1285. float dist;
  1286. float frac;
  1287. int i;
  1288. if ( state == LAUNCHED && !unGuided ) {
  1289. GetSeekPos( seekPos );
  1290. if ( rndUpdateTime < gameLocal.time ) {
  1291. rndAng[ 0 ] = rndScale[ 0 ] * gameLocal.random.CRandomFloat();
  1292. rndAng[ 1 ] = rndScale[ 1 ] * gameLocal.random.CRandomFloat();
  1293. rndAng[ 2 ] = rndScale[ 2 ] * gameLocal.random.CRandomFloat();
  1294. rndUpdateTime = gameLocal.time + 200;
  1295. }
  1296. nose = physicsObj.GetOrigin() + 10.0f * physicsObj.GetAxis()[0];
  1297. dir = seekPos - nose;
  1298. dist = dir.Normalize();
  1299. dirAng = dir.ToAngles();
  1300. // make it more accurate as it gets closer
  1301. frac = dist / clamp_dist;
  1302. if ( frac > 1.0f ) {
  1303. frac = 1.0f;
  1304. }
  1305. diff = dirAng - angles + rndAng * frac;
  1306. // clamp the to the max turn rate
  1307. diff.Normalize180();
  1308. for( i = 0; i < 3; i++ ) {
  1309. if ( diff[ i ] > turn_max ) {
  1310. diff[ i ] = turn_max;
  1311. } else if ( diff[ i ] < -turn_max ) {
  1312. diff[ i ] = -turn_max;
  1313. }
  1314. }
  1315. angles += diff;
  1316. // make the visual model always points the dir we're traveling
  1317. dir = angles.ToForward();
  1318. velocity = dir * speed;
  1319. if ( burstMode && dist < burstDist ) {
  1320. unGuided = true;
  1321. velocity *= burstVelocity;
  1322. }
  1323. physicsObj.SetLinearVelocity( velocity );
  1324. // align z-axis of model with the direction
  1325. axis = dir.ToMat3();
  1326. tmp = axis[2];
  1327. axis[2] = axis[0];
  1328. axis[0] = -tmp;
  1329. GetPhysics()->SetAxis( axis );
  1330. }
  1331. idProjectile::Think();
  1332. }
  1333. /*
  1334. =================
  1335. idGuidedProjectile::Launch
  1336. =================
  1337. */
  1338. void idGuidedProjectile::Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire, const float launchPower, float dmgPower ) {
  1339. idProjectile::Launch( start, dir, pushVelocity, timeSinceFire, launchPower, dmgPower );
  1340. if ( owner.GetEntity() ) {
  1341. if ( owner.GetEntity()->IsType( idAI::Type ) ) {
  1342. enemy = static_cast<idAI *>( owner.GetEntity() )->GetEnemy();
  1343. } else if ( owner.GetEntity()->IsType( idPlayer::Type ) ) {
  1344. trace_t tr;
  1345. idPlayer *player = static_cast<idPlayer*>( owner.GetEntity() );
  1346. idVec3 start = player->GetEyePosition();
  1347. idVec3 end = start + player->viewAxis[0] * 1000.0f;
  1348. gameLocal.clip.TracePoint( tr, start, end, MASK_SHOT_RENDERMODEL | CONTENTS_BODY, owner.GetEntity() );
  1349. if ( tr.fraction < 1.0f ) {
  1350. enemy = gameLocal.GetTraceEntity( tr );
  1351. }
  1352. // ignore actors on the player's team
  1353. if ( enemy.GetEntity() == NULL || !enemy.GetEntity()->IsType( idActor::Type ) || ( static_cast<idActor *>( enemy.GetEntity() )->team == player->team ) ) {
  1354. enemy = player->EnemyWithMostHealth();
  1355. }
  1356. }
  1357. }
  1358. const idVec3 &vel = physicsObj.GetLinearVelocity();
  1359. angles = vel.ToAngles();
  1360. speed = vel.Length();
  1361. rndScale = spawnArgs.GetAngles( "random", "15 15 0" );
  1362. turn_max = spawnArgs.GetFloat( "turn_max", "180" ) / ( float )USERCMD_HZ;
  1363. clamp_dist = spawnArgs.GetFloat( "clamp_dist", "256" );
  1364. burstMode = spawnArgs.GetBool( "burstMode" );
  1365. unGuided = false;
  1366. burstDist = spawnArgs.GetFloat( "burstDist", "64" );
  1367. burstVelocity = spawnArgs.GetFloat( "burstVelocity", "1.25" );
  1368. UpdateVisuals();
  1369. }
  1370. #ifdef _D3XP
  1371. void idGuidedProjectile::SetEnemy( idEntity *ent ) {
  1372. enemy = ent;
  1373. }
  1374. void idGuidedProjectile::Event_SetEnemy(idEntity *ent) {
  1375. SetEnemy(ent);
  1376. }
  1377. #endif
  1378. /*
  1379. ===============================================================================
  1380. idSoulCubeMissile
  1381. ===============================================================================
  1382. */
  1383. CLASS_DECLARATION( idGuidedProjectile, idSoulCubeMissile )
  1384. END_CLASS
  1385. /*
  1386. ================
  1387. idSoulCubeMissile::Spawn( void )
  1388. ================
  1389. */
  1390. void idSoulCubeMissile::Spawn( void ) {
  1391. startingVelocity.Zero();
  1392. endingVelocity.Zero();
  1393. accelTime = 0.0f;
  1394. launchTime = 0;
  1395. killPhase = false;
  1396. returnPhase = false;
  1397. smokeKillTime = 0;
  1398. smokeKill = NULL;
  1399. }
  1400. /*
  1401. =================
  1402. idSoulCubeMissile::~idSoulCubeMissile
  1403. =================
  1404. */
  1405. idSoulCubeMissile::~idSoulCubeMissile() {
  1406. }
  1407. /*
  1408. ================
  1409. idSoulCubeMissile::Save
  1410. ================
  1411. */
  1412. void idSoulCubeMissile::Save( idSaveGame *savefile ) const {
  1413. savefile->WriteVec3( startingVelocity );
  1414. savefile->WriteVec3( endingVelocity );
  1415. savefile->WriteFloat( accelTime );
  1416. savefile->WriteInt( launchTime );
  1417. savefile->WriteBool( killPhase );
  1418. savefile->WriteBool( returnPhase );
  1419. savefile->WriteVec3( destOrg);
  1420. savefile->WriteInt( orbitTime );
  1421. savefile->WriteVec3( orbitOrg );
  1422. savefile->WriteInt( smokeKillTime );
  1423. savefile->WriteParticle( smokeKill );
  1424. }
  1425. /*
  1426. ================
  1427. idSoulCubeMissile::Restore
  1428. ================
  1429. */
  1430. void idSoulCubeMissile::Restore( idRestoreGame *savefile ) {
  1431. savefile->ReadVec3( startingVelocity );
  1432. savefile->ReadVec3( endingVelocity );
  1433. savefile->ReadFloat( accelTime );
  1434. savefile->ReadInt( launchTime );
  1435. savefile->ReadBool( killPhase );
  1436. savefile->ReadBool( returnPhase );
  1437. savefile->ReadVec3( destOrg);
  1438. savefile->ReadInt( orbitTime );
  1439. savefile->ReadVec3( orbitOrg );
  1440. savefile->ReadInt( smokeKillTime );
  1441. savefile->ReadParticle( smokeKill );
  1442. }
  1443. /*
  1444. ================
  1445. idSoulCubeMissile::KillTarget
  1446. ================
  1447. */
  1448. void idSoulCubeMissile::KillTarget( const idVec3 &dir ) {
  1449. idEntity *ownerEnt;
  1450. const char *smokeName;
  1451. idActor *act;
  1452. ReturnToOwner();
  1453. if ( enemy.GetEntity() && enemy.GetEntity()->IsType( idActor::Type ) ) {
  1454. act = static_cast<idActor*>( enemy.GetEntity() );
  1455. killPhase = true;
  1456. orbitOrg = act->GetPhysics()->GetAbsBounds().GetCenter();
  1457. orbitTime = gameLocal.time;
  1458. smokeKillTime = 0;
  1459. smokeName = spawnArgs.GetString( "smoke_kill" );
  1460. if ( *smokeName != '\0' ) {
  1461. smokeKill = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  1462. smokeKillTime = gameLocal.time;
  1463. }
  1464. ownerEnt = owner.GetEntity();
  1465. if ( ( act->health > 0 ) && ownerEnt && ownerEnt->IsType( idPlayer::Type ) && ( ownerEnt->health > 0 ) && !act->spawnArgs.GetBool( "boss" ) ) {
  1466. static_cast<idPlayer *>( ownerEnt )->GiveHealthPool( act->health );
  1467. }
  1468. act->Damage( this, owner.GetEntity(), dir, spawnArgs.GetString( "def_damage" ), 1.0f, INVALID_JOINT );
  1469. act->GetAFPhysics()->SetTimeScale( 0.25 );
  1470. StartSound( "snd_explode", SND_CHANNEL_BODY, 0, false, NULL );
  1471. }
  1472. }
  1473. /*
  1474. ================
  1475. idSoulCubeMissile::Think
  1476. ================
  1477. */
  1478. void idSoulCubeMissile::Think( void ) {
  1479. float pct;
  1480. idVec3 seekPos;
  1481. idEntity *ownerEnt;
  1482. if ( state == LAUNCHED ) {
  1483. if ( killPhase ) {
  1484. // orbit the mob, cascading down
  1485. if ( gameLocal.time < orbitTime + 1500 ) {
  1486. if ( !gameLocal.smokeParticles->EmitSmoke( smokeKill, smokeKillTime, gameLocal.random.CRandomFloat(), orbitOrg, mat3_identity, timeGroup /*_D3XP*/ ) ) {
  1487. smokeKillTime = gameLocal.time;
  1488. }
  1489. }
  1490. } else {
  1491. if ( accelTime && gameLocal.time < launchTime + accelTime * 1000 ) {
  1492. pct = ( gameLocal.time - launchTime ) / ( accelTime * 1000 );
  1493. speed = ( startingVelocity + ( startingVelocity + endingVelocity ) * pct ).Length();
  1494. }
  1495. }
  1496. idGuidedProjectile::Think();
  1497. GetSeekPos( seekPos );
  1498. if ( ( seekPos - physicsObj.GetOrigin() ).Length() < 32.0f ) {
  1499. if ( returnPhase ) {
  1500. StopSound( SND_CHANNEL_ANY, false );
  1501. StartSound( "snd_return", SND_CHANNEL_BODY2, 0, false, NULL );
  1502. Hide();
  1503. PostEventSec( &EV_Remove, 2.0f );
  1504. ownerEnt = owner.GetEntity();
  1505. if ( ownerEnt && ownerEnt->IsType( idPlayer::Type ) ) {
  1506. static_cast<idPlayer *>( ownerEnt )->SetSoulCubeProjectile( NULL );
  1507. }
  1508. state = FIZZLED;
  1509. } else if ( !killPhase ){
  1510. KillTarget( physicsObj.GetAxis()[0] );
  1511. }
  1512. }
  1513. }
  1514. }
  1515. /*
  1516. ================
  1517. idSoulCubeMissile::GetSeekPos
  1518. ================
  1519. */
  1520. void idSoulCubeMissile::GetSeekPos( idVec3 &out ) {
  1521. if ( returnPhase && owner.GetEntity() && owner.GetEntity()->IsType( idActor::Type ) ) {
  1522. idActor *act = static_cast<idActor*>( owner.GetEntity() );
  1523. out = act->GetEyePosition();
  1524. return;
  1525. }
  1526. if ( destOrg != vec3_zero ) {
  1527. out = destOrg;
  1528. return;
  1529. }
  1530. idGuidedProjectile::GetSeekPos( out );
  1531. }
  1532. /*
  1533. ================
  1534. idSoulCubeMissile::Event_ReturnToOwner
  1535. ================
  1536. */
  1537. void idSoulCubeMissile::ReturnToOwner() {
  1538. speed *= 0.65f;
  1539. killPhase = false;
  1540. returnPhase = true;
  1541. smokeFlyTime = 0;
  1542. }
  1543. /*
  1544. =================
  1545. idSoulCubeMissile::Launch
  1546. =================
  1547. */
  1548. void idSoulCubeMissile::Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire, const float launchPower, float dmgPower ) {
  1549. idVec3 newStart;
  1550. idVec3 offs;
  1551. idEntity *ownerEnt;
  1552. // push it out a little
  1553. newStart = start + dir * spawnArgs.GetFloat( "launchDist" );
  1554. offs = spawnArgs.GetVector( "launchOffset", "0 0 -4" );
  1555. newStart += offs;
  1556. idGuidedProjectile::Launch( newStart, dir, pushVelocity, timeSinceFire, launchPower, dmgPower );
  1557. if ( enemy.GetEntity() == NULL || !enemy.GetEntity()->IsType( idActor::Type ) ) {
  1558. destOrg = start + dir * 256.0f;
  1559. } else {
  1560. destOrg.Zero();
  1561. }
  1562. physicsObj.SetClipMask( 0 ); // never collide.. think routine will decide when to detonate
  1563. startingVelocity = spawnArgs.GetVector( "startingVelocity", "15 0 0" );
  1564. endingVelocity = spawnArgs.GetVector( "endingVelocity", "1500 0 0" );
  1565. accelTime = spawnArgs.GetFloat( "accelTime", "5" );
  1566. physicsObj.SetLinearVelocity( startingVelocity.Length() * physicsObj.GetAxis()[2] );
  1567. launchTime = gameLocal.time;
  1568. killPhase = false;
  1569. UpdateVisuals();
  1570. ownerEnt = owner.GetEntity();
  1571. if ( ownerEnt && ownerEnt->IsType( idPlayer::Type ) ) {
  1572. static_cast<idPlayer *>( ownerEnt )->SetSoulCubeProjectile( this );
  1573. }
  1574. }
  1575. /*
  1576. ===============================================================================
  1577. idBFGProjectile
  1578. ===============================================================================
  1579. */
  1580. const idEventDef EV_RemoveBeams( "<removeBeams>", NULL );
  1581. CLASS_DECLARATION( idProjectile, idBFGProjectile )
  1582. EVENT( EV_RemoveBeams, idBFGProjectile::Event_RemoveBeams )
  1583. END_CLASS
  1584. /*
  1585. =================
  1586. idBFGProjectile::idBFGProjectile
  1587. =================
  1588. */
  1589. idBFGProjectile::idBFGProjectile() {
  1590. memset( &secondModel, 0, sizeof( secondModel ) );
  1591. secondModelDefHandle = -1;
  1592. nextDamageTime = 0;
  1593. }
  1594. /*
  1595. =================
  1596. idBFGProjectile::~idBFGProjectile
  1597. =================
  1598. */
  1599. idBFGProjectile::~idBFGProjectile() {
  1600. FreeBeams();
  1601. if ( secondModelDefHandle >= 0 ) {
  1602. gameRenderWorld->FreeEntityDef( secondModelDefHandle );
  1603. secondModelDefHandle = -1;
  1604. }
  1605. }
  1606. /*
  1607. ================
  1608. idBFGProjectile::Spawn
  1609. ================
  1610. */
  1611. void idBFGProjectile::Spawn( void ) {
  1612. beamTargets.Clear();
  1613. memset( &secondModel, 0, sizeof( secondModel ) );
  1614. secondModelDefHandle = -1;
  1615. const char *temp = spawnArgs.GetString( "model_two" );
  1616. if ( temp && *temp ) {
  1617. secondModel.hModel = renderModelManager->FindModel( temp );
  1618. secondModel.bounds = secondModel.hModel->Bounds( &secondModel );
  1619. secondModel.shaderParms[ SHADERPARM_RED ] =
  1620. secondModel.shaderParms[ SHADERPARM_GREEN ] =
  1621. secondModel.shaderParms[ SHADERPARM_BLUE ] =
  1622. secondModel.shaderParms[ SHADERPARM_ALPHA ] = 1.0f;
  1623. secondModel.noSelfShadow = true;
  1624. secondModel.noShadow = true;
  1625. }
  1626. nextDamageTime = 0;
  1627. damageFreq = NULL;
  1628. }
  1629. /*
  1630. ================
  1631. idBFGProjectile::Save
  1632. ================
  1633. */
  1634. void idBFGProjectile::Save( idSaveGame *savefile ) const {
  1635. int i;
  1636. savefile->WriteInt( beamTargets.Num() );
  1637. for ( i = 0; i < beamTargets.Num(); i++ ) {
  1638. beamTargets[i].target.Save( savefile );
  1639. savefile->WriteRenderEntity( beamTargets[i].renderEntity );
  1640. savefile->WriteInt( beamTargets[i].modelDefHandle );
  1641. }
  1642. savefile->WriteRenderEntity( secondModel );
  1643. savefile->WriteInt( secondModelDefHandle );
  1644. savefile->WriteInt( nextDamageTime );
  1645. savefile->WriteString( damageFreq );
  1646. }
  1647. /*
  1648. ================
  1649. idBFGProjectile::Restore
  1650. ================
  1651. */
  1652. void idBFGProjectile::Restore( idRestoreGame *savefile ) {
  1653. int i, num;
  1654. savefile->ReadInt( num );
  1655. beamTargets.SetNum( num );
  1656. for ( i = 0; i < num; i++ ) {
  1657. beamTargets[i].target.Restore( savefile );
  1658. savefile->ReadRenderEntity( beamTargets[i].renderEntity );
  1659. savefile->ReadInt( beamTargets[i].modelDefHandle );
  1660. if ( beamTargets[i].modelDefHandle >= 0 ) {
  1661. beamTargets[i].modelDefHandle = gameRenderWorld->AddEntityDef( &beamTargets[i].renderEntity );
  1662. }
  1663. }
  1664. savefile->ReadRenderEntity( secondModel );
  1665. savefile->ReadInt( secondModelDefHandle );
  1666. savefile->ReadInt( nextDamageTime );
  1667. savefile->ReadString( damageFreq );
  1668. if ( secondModelDefHandle >= 0 ) {
  1669. secondModelDefHandle = gameRenderWorld->AddEntityDef( &secondModel );
  1670. }
  1671. }
  1672. /*
  1673. =================
  1674. idBFGProjectile::FreeBeams
  1675. =================
  1676. */
  1677. void idBFGProjectile::FreeBeams() {
  1678. for ( int i = 0; i < beamTargets.Num(); i++ ) {
  1679. if ( beamTargets[i].modelDefHandle >= 0 ) {
  1680. gameRenderWorld->FreeEntityDef( beamTargets[i].modelDefHandle );
  1681. beamTargets[i].modelDefHandle = -1;
  1682. }
  1683. }
  1684. idPlayer *player = gameLocal.GetLocalPlayer();
  1685. if ( player ) {
  1686. player->playerView.EnableBFGVision( false );
  1687. }
  1688. }
  1689. /*
  1690. ================
  1691. idBFGProjectile::Think
  1692. ================
  1693. */
  1694. void idBFGProjectile::Think( void ) {
  1695. if ( state == LAUNCHED ) {
  1696. // update beam targets
  1697. for ( int i = 0; i < beamTargets.Num(); i++ ) {
  1698. if ( beamTargets[i].target.GetEntity() == NULL ) {
  1699. continue;
  1700. }
  1701. idPlayer *player = ( beamTargets[i].target.GetEntity()->IsType( idPlayer::Type ) ) ? static_cast<idPlayer*>( beamTargets[i].target.GetEntity() ) : NULL;
  1702. #ifdef _D3XP
  1703. // Major hack for end boss. :(
  1704. idAnimatedEntity *beamEnt;
  1705. idVec3 org;
  1706. bool forceDamage = false;
  1707. beamEnt = static_cast<idAnimatedEntity*>(beamTargets[i].target.GetEntity());
  1708. if ( !idStr::Cmp( beamEnt->GetEntityDefName(), "monster_boss_d3xp_maledict" ) ) {
  1709. SetTimeState ts( beamEnt->timeGroup );
  1710. idMat3 temp;
  1711. jointHandle_t bodyJoint;
  1712. bodyJoint = beamEnt->GetAnimator()->GetJointHandle( "Chest1" );
  1713. beamEnt->GetJointWorldTransform( bodyJoint, gameLocal.time, org, temp );
  1714. forceDamage = true;
  1715. } else {
  1716. org = beamEnt->GetPhysics()->GetAbsBounds().GetCenter();
  1717. }
  1718. #else
  1719. idVec3 org = beamTargets[i].target.GetEntity()->GetPhysics()->GetAbsBounds().GetCenter();
  1720. #endif
  1721. beamTargets[i].renderEntity.origin = GetPhysics()->GetOrigin();
  1722. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_BEAM_END_X ] = org.x;
  1723. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_BEAM_END_Y ] = org.y;
  1724. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_BEAM_END_Z ] = org.z;
  1725. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_RED ] =
  1726. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_GREEN ] =
  1727. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_BLUE ] =
  1728. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_ALPHA ] = 1.0f;
  1729. if ( gameLocal.time > nextDamageTime ) {
  1730. bool bfgVision = true;
  1731. #ifdef _D3XP
  1732. if ( damageFreq && *(const char *)damageFreq && beamTargets[i].target.GetEntity() && ( forceDamage || beamTargets[i].target.GetEntity()->CanDamage( GetPhysics()->GetOrigin(), org ) ) ) {
  1733. #else
  1734. if ( damageFreq && *(const char *)damageFreq && beamTargets[i].target.GetEntity() && beamTargets[i].target.GetEntity()->CanDamage( GetPhysics()->GetOrigin(), org ) ) {
  1735. #endif
  1736. org = beamTargets[i].target.GetEntity()->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
  1737. org.Normalize();
  1738. beamTargets[i].target.GetEntity()->Damage( this, owner.GetEntity(), org, damageFreq, ( damagePower ) ? damagePower : 1.0f, INVALID_JOINT );
  1739. } else {
  1740. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_RED ] =
  1741. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_GREEN ] =
  1742. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_BLUE ] =
  1743. beamTargets[i].renderEntity.shaderParms[ SHADERPARM_ALPHA ] = 0.0f;
  1744. bfgVision = false;
  1745. }
  1746. if ( player ) {
  1747. player->playerView.EnableBFGVision( bfgVision );
  1748. }
  1749. nextDamageTime = gameLocal.time + BFG_DAMAGE_FREQUENCY;
  1750. }
  1751. gameRenderWorld->UpdateEntityDef( beamTargets[i].modelDefHandle, &beamTargets[i].renderEntity );
  1752. }
  1753. if ( secondModelDefHandle >= 0 ) {
  1754. secondModel.origin = GetPhysics()->GetOrigin();
  1755. gameRenderWorld->UpdateEntityDef( secondModelDefHandle, &secondModel );
  1756. }
  1757. idAngles ang;
  1758. ang.pitch = ( gameLocal.time & 4095 ) * 360.0f / -4096.0f;
  1759. ang.yaw = ang.pitch;
  1760. ang.roll = 0.0f;
  1761. SetAngles( ang );
  1762. ang.pitch = ( gameLocal.time & 2047 ) * 360.0f / -2048.0f;
  1763. ang.yaw = ang.pitch;
  1764. ang.roll = 0.0f;
  1765. secondModel.axis = ang.ToMat3();
  1766. UpdateVisuals();
  1767. }
  1768. idProjectile::Think();
  1769. }
  1770. /*
  1771. =================
  1772. idBFGProjectile::Launch
  1773. =================
  1774. */
  1775. void idBFGProjectile::Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire, const float power, const float dmgPower ) {
  1776. idProjectile::Launch( start, dir, pushVelocity, 0.0f, power, dmgPower );
  1777. // dmgPower * radius is the target acquisition area
  1778. // acquisition should make sure that monsters are not dormant
  1779. // which will cut down on hitting monsters not actively fighting
  1780. // but saves on the traces making sure they are visible
  1781. // damage is not applied until the projectile explodes
  1782. idEntity * ent;
  1783. idEntity * entityList[ MAX_GENTITIES ];
  1784. int numListedEntities;
  1785. idBounds bounds;
  1786. idVec3 damagePoint;
  1787. float radius;
  1788. spawnArgs.GetFloat( "damageRadius", "512", radius );
  1789. bounds = idBounds( GetPhysics()->GetOrigin() ).Expand( radius );
  1790. float beamWidth = spawnArgs.GetFloat( "beam_WidthFly" );
  1791. const char *skin = spawnArgs.GetString( "skin_beam" );
  1792. memset( &secondModel, 0, sizeof( secondModel ) );
  1793. secondModelDefHandle = -1;
  1794. const char *temp = spawnArgs.GetString( "model_two" );
  1795. if ( temp && *temp ) {
  1796. secondModel.hModel = renderModelManager->FindModel( temp );
  1797. secondModel.bounds = secondModel.hModel->Bounds( &secondModel );
  1798. secondModel.shaderParms[ SHADERPARM_RED ] =
  1799. secondModel.shaderParms[ SHADERPARM_GREEN ] =
  1800. secondModel.shaderParms[ SHADERPARM_BLUE ] =
  1801. secondModel.shaderParms[ SHADERPARM_ALPHA ] = 1.0f;
  1802. secondModel.noSelfShadow = true;
  1803. secondModel.noShadow = true;
  1804. secondModel.origin = GetPhysics()->GetOrigin();
  1805. secondModel.axis = GetPhysics()->GetAxis();
  1806. secondModelDefHandle = gameRenderWorld->AddEntityDef( &secondModel );
  1807. }
  1808. idVec3 delta( 15.0f, 15.0f, 15.0f );
  1809. //physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_LINEAR|EXTRAPOLATION_NOSTOP), gameLocal.time, 0, physicsObj.GetAxis().ToAngles(), delta, ang_zero );
  1810. // get all entities touching the bounds
  1811. numListedEntities = gameLocal.clip.EntitiesTouchingBounds( bounds, CONTENTS_BODY, entityList, MAX_GENTITIES );
  1812. for ( int e = 0; e < numListedEntities; e++ ) {
  1813. ent = entityList[ e ];
  1814. assert( ent );
  1815. if ( ent == this || ent == owner.GetEntity() || ent->IsHidden() || !ent->IsActive() || !ent->fl.takedamage || ent->health <= 0 || !ent->IsType( idActor::Type ) ) {
  1816. continue;
  1817. }
  1818. if ( !ent->CanDamage( GetPhysics()->GetOrigin(), damagePoint ) ) {
  1819. continue;
  1820. }
  1821. if ( ent->IsType( idPlayer::Type ) ) {
  1822. idPlayer *player = static_cast<idPlayer*>( ent );
  1823. player->playerView.EnableBFGVision( true );
  1824. }
  1825. beamTarget_t bt;
  1826. memset( &bt.renderEntity, 0, sizeof( renderEntity_t ) );
  1827. bt.renderEntity.origin = GetPhysics()->GetOrigin();
  1828. bt.renderEntity.axis = GetPhysics()->GetAxis();
  1829. bt.renderEntity.shaderParms[ SHADERPARM_BEAM_WIDTH ] = beamWidth;
  1830. bt.renderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
  1831. bt.renderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f;
  1832. bt.renderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
  1833. bt.renderEntity.shaderParms[ SHADERPARM_ALPHA ] = 1.0f;
  1834. bt.renderEntity.shaderParms[ SHADERPARM_DIVERSITY] = gameLocal.random.CRandomFloat() * 0.75;
  1835. bt.renderEntity.hModel = renderModelManager->FindModel( "_beam" );
  1836. bt.renderEntity.callback = NULL;
  1837. bt.renderEntity.numJoints = 0;
  1838. bt.renderEntity.joints = NULL;
  1839. bt.renderEntity.bounds.Clear();
  1840. bt.renderEntity.customSkin = declManager->FindSkin( skin );
  1841. bt.target = ent;
  1842. bt.modelDefHandle = gameRenderWorld->AddEntityDef( &bt.renderEntity );
  1843. beamTargets.Append( bt );
  1844. }
  1845. #ifdef _D3XP
  1846. // Major hack for end boss. :(
  1847. idAnimatedEntity *maledict = static_cast<idAnimatedEntity*>(gameLocal.FindEntity( "monster_boss_d3xp_maledict_1" ));
  1848. if ( maledict ) {
  1849. SetTimeState ts( maledict->timeGroup );
  1850. idVec3 realPoint;
  1851. idMat3 temp;
  1852. float dist;
  1853. jointHandle_t bodyJoint;
  1854. bodyJoint = maledict->GetAnimator()->GetJointHandle( "Chest1" );
  1855. maledict->GetJointWorldTransform( bodyJoint, gameLocal.time, realPoint, temp );
  1856. dist = idVec3( realPoint - GetPhysics()->GetOrigin() ).Length();
  1857. if ( dist < radius ) {
  1858. beamTarget_t bt;
  1859. memset( &bt.renderEntity, 0, sizeof( renderEntity_t ) );
  1860. bt.renderEntity.origin = GetPhysics()->GetOrigin();
  1861. bt.renderEntity.axis = GetPhysics()->GetAxis();
  1862. bt.renderEntity.shaderParms[ SHADERPARM_BEAM_WIDTH ] = beamWidth;
  1863. bt.renderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
  1864. bt.renderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f;
  1865. bt.renderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
  1866. bt.renderEntity.shaderParms[ SHADERPARM_ALPHA ] = 1.0f;
  1867. bt.renderEntity.shaderParms[ SHADERPARM_DIVERSITY] = gameLocal.random.CRandomFloat() * 0.75;
  1868. bt.renderEntity.hModel = renderModelManager->FindModel( "_beam" );
  1869. bt.renderEntity.callback = NULL;
  1870. bt.renderEntity.numJoints = 0;
  1871. bt.renderEntity.joints = NULL;
  1872. bt.renderEntity.bounds.Clear();
  1873. bt.renderEntity.customSkin = declManager->FindSkin( skin );
  1874. bt.target = maledict;
  1875. bt.modelDefHandle = gameRenderWorld->AddEntityDef( &bt.renderEntity );
  1876. beamTargets.Append( bt );
  1877. numListedEntities++;
  1878. }
  1879. }
  1880. #endif
  1881. if ( numListedEntities ) {
  1882. StartSound( "snd_beam", SND_CHANNEL_BODY2, 0, false, NULL );
  1883. }
  1884. damageFreq = spawnArgs.GetString( "def_damageFreq" );
  1885. nextDamageTime = gameLocal.time + BFG_DAMAGE_FREQUENCY;
  1886. UpdateVisuals();
  1887. }
  1888. /*
  1889. ================
  1890. idProjectile::Event_RemoveBeams
  1891. ================
  1892. */
  1893. void idBFGProjectile::Event_RemoveBeams() {
  1894. FreeBeams();
  1895. UpdateVisuals();
  1896. }
  1897. /*
  1898. ================
  1899. idProjectile::Explode
  1900. ================
  1901. */
  1902. void idBFGProjectile::Explode( const trace_t &collision, idEntity *ignore ) {
  1903. int i;
  1904. idVec3 dmgPoint;
  1905. idVec3 dir;
  1906. float beamWidth;
  1907. float damageScale;
  1908. const char *damage;
  1909. idPlayer * player;
  1910. idEntity * ownerEnt;
  1911. ownerEnt = owner.GetEntity();
  1912. if ( ownerEnt && ownerEnt->IsType( idPlayer::Type ) ) {
  1913. player = static_cast< idPlayer * >( ownerEnt );
  1914. } else {
  1915. player = NULL;
  1916. }
  1917. beamWidth = spawnArgs.GetFloat( "beam_WidthExplode" );
  1918. damage = spawnArgs.GetString( "def_damage" );
  1919. for ( i = 0; i < beamTargets.Num(); i++ ) {
  1920. if ( ( beamTargets[i].target.GetEntity() == NULL ) || ( ownerEnt == NULL ) ) {
  1921. continue;
  1922. }
  1923. if ( !beamTargets[i].target.GetEntity()->CanDamage( GetPhysics()->GetOrigin(), dmgPoint ) ) {
  1924. continue;
  1925. }
  1926. beamTargets[i].renderEntity.shaderParms[SHADERPARM_BEAM_WIDTH] = beamWidth;
  1927. // if the hit entity takes damage
  1928. if ( damagePower ) {
  1929. damageScale = damagePower;
  1930. } else {
  1931. damageScale = 1.0f;
  1932. }
  1933. // if the projectile owner is a player
  1934. if ( player ) {
  1935. // if the projectile hit an actor
  1936. if ( beamTargets[i].target.GetEntity()->IsType( idActor::Type ) ) {
  1937. player->SetLastHitTime( gameLocal.time );
  1938. player->AddProjectileHits( 1 );
  1939. damageScale *= player->PowerUpModifier( PROJECTILE_DAMAGE );
  1940. }
  1941. }
  1942. if ( damage[0] && ( beamTargets[i].target.GetEntity()->entityNumber > gameLocal.numClients - 1 ) ) {
  1943. dir = beamTargets[i].target.GetEntity()->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
  1944. dir.Normalize();
  1945. beamTargets[i].target.GetEntity()->Damage( this, ownerEnt, dir, damage, damageScale, ( collision.c.id < 0 ) ? CLIPMODEL_ID_TO_JOINT_HANDLE( collision.c.id ) : INVALID_JOINT );
  1946. }
  1947. }
  1948. if ( secondModelDefHandle >= 0 ) {
  1949. gameRenderWorld->FreeEntityDef( secondModelDefHandle );
  1950. secondModelDefHandle = -1;
  1951. }
  1952. if ( ignore == NULL ) {
  1953. projectileFlags.noSplashDamage = true;
  1954. }
  1955. if ( !gameLocal.isClient ) {
  1956. if ( ignore != NULL ) {
  1957. PostEventMS( &EV_RemoveBeams, 750 );
  1958. } else {
  1959. PostEventMS( &EV_RemoveBeams, 0 );
  1960. }
  1961. }
  1962. return idProjectile::Explode( collision, ignore );
  1963. }
  1964. /*
  1965. ===============================================================================
  1966. idDebris
  1967. ===============================================================================
  1968. */
  1969. CLASS_DECLARATION( idEntity, idDebris )
  1970. EVENT( EV_Explode, idDebris::Event_Explode )
  1971. EVENT( EV_Fizzle, idDebris::Event_Fizzle )
  1972. END_CLASS
  1973. /*
  1974. ================
  1975. idDebris::Spawn
  1976. ================
  1977. */
  1978. void idDebris::Spawn( void ) {
  1979. owner = NULL;
  1980. smokeFly = NULL;
  1981. smokeFlyTime = 0;
  1982. }
  1983. /*
  1984. ================
  1985. idDebris::Create
  1986. ================
  1987. */
  1988. void idDebris::Create( idEntity *owner, const idVec3 &start, const idMat3 &axis ) {
  1989. Unbind();
  1990. GetPhysics()->SetOrigin( start );
  1991. GetPhysics()->SetAxis( axis );
  1992. GetPhysics()->SetContents( 0 );
  1993. this->owner = owner;
  1994. smokeFly = NULL;
  1995. smokeFlyTime = 0;
  1996. sndBounce = NULL;
  1997. #ifdef _D3XP
  1998. noGrab = true;
  1999. #endif
  2000. UpdateVisuals();
  2001. }
  2002. /*
  2003. =================
  2004. idDebris::idDebris
  2005. =================
  2006. */
  2007. idDebris::idDebris( void ) {
  2008. owner = NULL;
  2009. smokeFly = NULL;
  2010. smokeFlyTime = 0;
  2011. sndBounce = NULL;
  2012. }
  2013. /*
  2014. =================
  2015. idDebris::~idDebris
  2016. =================
  2017. */
  2018. idDebris::~idDebris( void ) {
  2019. }
  2020. /*
  2021. =================
  2022. idDebris::Save
  2023. =================
  2024. */
  2025. void idDebris::Save( idSaveGame *savefile ) const {
  2026. owner.Save( savefile );
  2027. savefile->WriteStaticObject( physicsObj );
  2028. savefile->WriteParticle( smokeFly );
  2029. savefile->WriteInt( smokeFlyTime );
  2030. savefile->WriteSoundShader( sndBounce );
  2031. }
  2032. /*
  2033. =================
  2034. idDebris::Restore
  2035. =================
  2036. */
  2037. void idDebris::Restore( idRestoreGame *savefile ) {
  2038. owner.Restore( savefile );
  2039. savefile->ReadStaticObject( physicsObj );
  2040. RestorePhysics( &physicsObj );
  2041. savefile->ReadParticle( smokeFly );
  2042. savefile->ReadInt( smokeFlyTime );
  2043. savefile->ReadSoundShader( sndBounce );
  2044. }
  2045. /*
  2046. =================
  2047. idDebris::Launch
  2048. =================
  2049. */
  2050. void idDebris::Launch( void ) {
  2051. float fuse;
  2052. idVec3 velocity;
  2053. idAngles angular_velocity;
  2054. float linear_friction;
  2055. float angular_friction;
  2056. float contact_friction;
  2057. float bounce;
  2058. float mass;
  2059. float gravity;
  2060. idVec3 gravVec;
  2061. bool randomVelocity;
  2062. idMat3 axis;
  2063. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  2064. spawnArgs.GetVector( "velocity", "0 0 0", velocity );
  2065. spawnArgs.GetAngles( "angular_velocity", "0 0 0", angular_velocity );
  2066. linear_friction = spawnArgs.GetFloat( "linear_friction" );
  2067. angular_friction = spawnArgs.GetFloat( "angular_friction" );
  2068. contact_friction = spawnArgs.GetFloat( "contact_friction" );
  2069. bounce = spawnArgs.GetFloat( "bounce" );
  2070. mass = spawnArgs.GetFloat( "mass" );
  2071. gravity = spawnArgs.GetFloat( "gravity" );
  2072. fuse = spawnArgs.GetFloat( "fuse" );
  2073. randomVelocity = spawnArgs.GetBool ( "random_velocity" );
  2074. if ( mass <= 0 ) {
  2075. gameLocal.Error( "Invalid mass on '%s'\n", GetEntityDefName() );
  2076. }
  2077. if ( randomVelocity ) {
  2078. velocity.x *= gameLocal.random.RandomFloat() + 0.5f;
  2079. velocity.y *= gameLocal.random.RandomFloat() + 0.5f;
  2080. velocity.z *= gameLocal.random.RandomFloat() + 0.5f;
  2081. }
  2082. if ( health ) {
  2083. fl.takedamage = true;
  2084. }
  2085. gravVec = gameLocal.GetGravity();
  2086. gravVec.NormalizeFast();
  2087. axis = GetPhysics()->GetAxis();
  2088. Unbind();
  2089. physicsObj.SetSelf( this );
  2090. // check if a clip model is set
  2091. const char *clipModelName;
  2092. idTraceModel trm;
  2093. spawnArgs.GetString( "clipmodel", "", &clipModelName );
  2094. if ( !clipModelName[0] ) {
  2095. clipModelName = spawnArgs.GetString( "model" ); // use the visual model
  2096. }
  2097. // load the trace model
  2098. if ( !collisionModelManager->TrmFromModel( clipModelName, trm ) ) {
  2099. // default to a box
  2100. physicsObj.SetClipBox( renderEntity.bounds, 1.0f );
  2101. } else {
  2102. physicsObj.SetClipModel( new idClipModel( trm ), 1.0f );
  2103. }
  2104. physicsObj.GetClipModel()->SetOwner( owner.GetEntity() );
  2105. physicsObj.SetMass( mass );
  2106. physicsObj.SetFriction( linear_friction, angular_friction, contact_friction );
  2107. if ( contact_friction == 0.0f ) {
  2108. physicsObj.NoContact();
  2109. }
  2110. physicsObj.SetBouncyness( bounce );
  2111. physicsObj.SetGravity( gravVec * gravity );
  2112. physicsObj.SetContents( 0 );
  2113. physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP );
  2114. physicsObj.SetLinearVelocity( axis[ 0 ] * velocity[ 0 ] + axis[ 1 ] * velocity[ 1 ] + axis[ 2 ] * velocity[ 2 ] );
  2115. physicsObj.SetAngularVelocity( angular_velocity.ToAngularVelocity() * axis );
  2116. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  2117. physicsObj.SetAxis( axis );
  2118. SetPhysics( &physicsObj );
  2119. if ( !gameLocal.isClient ) {
  2120. if ( fuse <= 0 ) {
  2121. // run physics for 1 second
  2122. RunPhysics();
  2123. PostEventMS( &EV_Remove, 0 );
  2124. } else if ( spawnArgs.GetBool( "detonate_on_fuse" ) ) {
  2125. if ( fuse < 0.0f ) {
  2126. fuse = 0.0f;
  2127. }
  2128. RunPhysics();
  2129. PostEventSec( &EV_Explode, fuse );
  2130. } else {
  2131. if ( fuse < 0.0f ) {
  2132. fuse = 0.0f;
  2133. }
  2134. PostEventSec( &EV_Fizzle, fuse );
  2135. }
  2136. }
  2137. StartSound( "snd_fly", SND_CHANNEL_BODY, 0, false, NULL );
  2138. smokeFly = NULL;
  2139. smokeFlyTime = 0;
  2140. const char *smokeName = spawnArgs.GetString( "smoke_fly" );
  2141. if ( *smokeName != '\0' ) {
  2142. smokeFly = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  2143. smokeFlyTime = gameLocal.time;
  2144. gameLocal.smokeParticles->EmitSmoke( smokeFly, smokeFlyTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ );
  2145. }
  2146. const char *sndName = spawnArgs.GetString( "snd_bounce" );
  2147. if ( *sndName != '\0' ) {
  2148. sndBounce = declManager->FindSound( sndName );
  2149. }
  2150. UpdateVisuals();
  2151. }
  2152. /*
  2153. ================
  2154. idDebris::Think
  2155. ================
  2156. */
  2157. void idDebris::Think( void ) {
  2158. // run physics
  2159. RunPhysics();
  2160. Present();
  2161. if ( smokeFly && smokeFlyTime ) {
  2162. if ( !gameLocal.smokeParticles->EmitSmoke( smokeFly, smokeFlyTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ ) ) {
  2163. smokeFlyTime = 0;
  2164. }
  2165. }
  2166. }
  2167. /*
  2168. ================
  2169. idDebris::Killed
  2170. ================
  2171. */
  2172. void idDebris::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  2173. if ( spawnArgs.GetBool( "detonate_on_death" ) ) {
  2174. Explode();
  2175. } else {
  2176. Fizzle();
  2177. }
  2178. }
  2179. /*
  2180. =================
  2181. idDebris::Collide
  2182. =================
  2183. */
  2184. bool idDebris::Collide( const trace_t &collision, const idVec3 &velocity ) {
  2185. if ( sndBounce != NULL ) {
  2186. StartSoundShader( sndBounce, SND_CHANNEL_BODY, 0, false, NULL );
  2187. }
  2188. sndBounce = NULL;
  2189. return false;
  2190. }
  2191. /*
  2192. ================
  2193. idDebris::Fizzle
  2194. ================
  2195. */
  2196. void idDebris::Fizzle( void ) {
  2197. if ( IsHidden() ) {
  2198. // already exploded
  2199. return;
  2200. }
  2201. StopSound( SND_CHANNEL_ANY, false );
  2202. StartSound( "snd_fizzle", SND_CHANNEL_BODY, 0, false, NULL );
  2203. // fizzle FX
  2204. const char *smokeName = spawnArgs.GetString( "smoke_fuse" );
  2205. if ( *smokeName != '\0' ) {
  2206. smokeFly = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  2207. smokeFlyTime = gameLocal.time;
  2208. gameLocal.smokeParticles->EmitSmoke( smokeFly, smokeFlyTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ );
  2209. }
  2210. fl.takedamage = false;
  2211. physicsObj.SetContents( 0 );
  2212. physicsObj.PutToRest();
  2213. Hide();
  2214. if ( gameLocal.isClient ) {
  2215. return;
  2216. }
  2217. CancelEvents( &EV_Fizzle );
  2218. PostEventMS( &EV_Remove, 0 );
  2219. }
  2220. /*
  2221. ================
  2222. idDebris::Explode
  2223. ================
  2224. */
  2225. void idDebris::Explode( void ) {
  2226. if ( IsHidden() ) {
  2227. // already exploded
  2228. return;
  2229. }
  2230. StopSound( SND_CHANNEL_ANY, false );
  2231. StartSound( "snd_explode", SND_CHANNEL_BODY, 0, false, NULL );
  2232. Hide();
  2233. // these must not be "live forever" particle systems
  2234. smokeFly = NULL;
  2235. smokeFlyTime = 0;
  2236. const char *smokeName = spawnArgs.GetString( "smoke_detonate" );
  2237. if ( *smokeName != '\0' ) {
  2238. smokeFly = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  2239. smokeFlyTime = gameLocal.time;
  2240. gameLocal.smokeParticles->EmitSmoke( smokeFly, smokeFlyTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ );
  2241. }
  2242. fl.takedamage = false;
  2243. physicsObj.SetContents( 0 );
  2244. physicsObj.PutToRest();
  2245. CancelEvents( &EV_Explode );
  2246. PostEventMS( &EV_Remove, 0 );
  2247. }
  2248. /*
  2249. ================
  2250. idDebris::Event_Explode
  2251. ================
  2252. */
  2253. void idDebris::Event_Explode( void ) {
  2254. Explode();
  2255. }
  2256. /*
  2257. ================
  2258. idDebris::Event_Fizzle
  2259. ================
  2260. */
  2261. void idDebris::Event_Fizzle( void ) {
  2262. Fizzle();
  2263. }