Projectile.cpp 83 KB

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