cg_weapons.c 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. //
  19. // cg_weapons.c -- events and effects dealing with weapons
  20. #include "cg_local.h"
  21. /*
  22. ==========================
  23. CG_MachineGunEjectBrass
  24. ==========================
  25. */
  26. static void CG_MachineGunEjectBrass( centity_t *cent ) {
  27. localEntity_t *le;
  28. refEntity_t *re;
  29. vec3_t velocity, xvelocity;
  30. vec3_t offset, xoffset;
  31. float waterScale = 1.0f;
  32. vec3_t v[3];
  33. if ( cg_brassTime.integer <= 0 ) {
  34. return;
  35. }
  36. le = CG_AllocLocalEntity();
  37. re = &le->refEntity;
  38. velocity[0] = 0;
  39. velocity[1] = -50 + 40 * crandom();
  40. velocity[2] = 100 + 50 * crandom();
  41. le->leType = LE_FRAGMENT;
  42. le->startTime = cg.time;
  43. le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random();
  44. le->pos.trType = TR_GRAVITY;
  45. le->pos.trTime = cg.time - (rand()&15);
  46. AnglesToAxis( cent->lerpAngles, v );
  47. offset[0] = 8;
  48. offset[1] = -4;
  49. offset[2] = 24;
  50. xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
  51. xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
  52. xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
  53. VectorAdd( cent->lerpOrigin, xoffset, re->origin );
  54. VectorCopy( re->origin, le->pos.trBase );
  55. if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
  56. waterScale = 0.10f;
  57. }
  58. xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
  59. xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
  60. xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
  61. VectorScale( xvelocity, waterScale, le->pos.trDelta );
  62. AxisCopy( axisDefault, re->axis );
  63. re->hModel = cgs.media.machinegunBrassModel;
  64. le->bounceFactor = 0.4 * waterScale;
  65. le->angles.trType = TR_LINEAR;
  66. le->angles.trTime = cg.time;
  67. le->angles.trBase[0] = rand()&31;
  68. le->angles.trBase[1] = rand()&31;
  69. le->angles.trBase[2] = rand()&31;
  70. le->angles.trDelta[0] = 2;
  71. le->angles.trDelta[1] = 1;
  72. le->angles.trDelta[2] = 0;
  73. le->leFlags = LEF_TUMBLE;
  74. le->leBounceSoundType = LEBS_BRASS;
  75. le->leMarkType = LEMT_NONE;
  76. }
  77. /*
  78. ==========================
  79. CG_ShotgunEjectBrass
  80. ==========================
  81. */
  82. static void CG_ShotgunEjectBrass( centity_t *cent ) {
  83. localEntity_t *le;
  84. refEntity_t *re;
  85. vec3_t velocity, xvelocity;
  86. vec3_t offset, xoffset;
  87. vec3_t v[3];
  88. int i;
  89. if ( cg_brassTime.integer <= 0 ) {
  90. return;
  91. }
  92. for ( i = 0; i < 2; i++ ) {
  93. float waterScale = 1.0f;
  94. le = CG_AllocLocalEntity();
  95. re = &le->refEntity;
  96. velocity[0] = 60 + 60 * crandom();
  97. if ( i == 0 ) {
  98. velocity[1] = 40 + 10 * crandom();
  99. } else {
  100. velocity[1] = -40 + 10 * crandom();
  101. }
  102. velocity[2] = 100 + 50 * crandom();
  103. le->leType = LE_FRAGMENT;
  104. le->startTime = cg.time;
  105. le->endTime = le->startTime + cg_brassTime.integer*3 + cg_brassTime.integer * random();
  106. le->pos.trType = TR_GRAVITY;
  107. le->pos.trTime = cg.time;
  108. AnglesToAxis( cent->lerpAngles, v );
  109. offset[0] = 8;
  110. offset[1] = 0;
  111. offset[2] = 24;
  112. xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
  113. xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
  114. xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
  115. VectorAdd( cent->lerpOrigin, xoffset, re->origin );
  116. VectorCopy( re->origin, le->pos.trBase );
  117. if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
  118. waterScale = 0.10f;
  119. }
  120. xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
  121. xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
  122. xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
  123. VectorScale( xvelocity, waterScale, le->pos.trDelta );
  124. AxisCopy( axisDefault, re->axis );
  125. re->hModel = cgs.media.shotgunBrassModel;
  126. le->bounceFactor = 0.3f;
  127. le->angles.trType = TR_LINEAR;
  128. le->angles.trTime = cg.time;
  129. le->angles.trBase[0] = rand()&31;
  130. le->angles.trBase[1] = rand()&31;
  131. le->angles.trBase[2] = rand()&31;
  132. le->angles.trDelta[0] = 1;
  133. le->angles.trDelta[1] = 0.5;
  134. le->angles.trDelta[2] = 0;
  135. le->leFlags = LEF_TUMBLE;
  136. le->leBounceSoundType = LEBS_BRASS;
  137. le->leMarkType = LEMT_NONE;
  138. }
  139. }
  140. #ifdef MISSIONPACK
  141. /*
  142. ==========================
  143. CG_NailgunEjectBrass
  144. ==========================
  145. */
  146. static void CG_NailgunEjectBrass( centity_t *cent ) {
  147. localEntity_t *smoke;
  148. vec3_t origin;
  149. vec3_t v[3];
  150. vec3_t offset;
  151. vec3_t xoffset;
  152. vec3_t up;
  153. AnglesToAxis( cent->lerpAngles, v );
  154. offset[0] = 0;
  155. offset[1] = -12;
  156. offset[2] = 24;
  157. xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
  158. xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
  159. xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
  160. VectorAdd( cent->lerpOrigin, xoffset, origin );
  161. VectorSet( up, 0, 0, 64 );
  162. smoke = CG_SmokePuff( origin, up, 32, 1, 1, 1, 0.33f, 700, cg.time, 0, 0, cgs.media.smokePuffShader );
  163. // use the optimized local entity add
  164. smoke->leType = LE_SCALE_FADE;
  165. }
  166. #endif
  167. /*
  168. ==========================
  169. CG_RailTrail
  170. ==========================
  171. */
  172. void CG_RailTrail (clientInfo_t *ci, vec3_t start, vec3_t end) {
  173. vec3_t axis[36], move, move2, next_move, vec, temp;
  174. float len;
  175. int i, j, skip;
  176. localEntity_t *le;
  177. refEntity_t *re;
  178. #define RADIUS 4
  179. #define ROTATION 1
  180. #define SPACING 5
  181. start[2] -= 4;
  182. VectorCopy (start, move);
  183. VectorSubtract (end, start, vec);
  184. len = VectorNormalize (vec);
  185. PerpendicularVector(temp, vec);
  186. for (i = 0 ; i < 36; i++) {
  187. RotatePointAroundVector(axis[i], vec, temp, i * 10);//banshee 2.4 was 10
  188. }
  189. le = CG_AllocLocalEntity();
  190. re = &le->refEntity;
  191. le->leType = LE_FADE_RGB;
  192. le->startTime = cg.time;
  193. le->endTime = cg.time + cg_railTrailTime.value;
  194. le->lifeRate = 1.0 / (le->endTime - le->startTime);
  195. re->shaderTime = cg.time / 1000.0f;
  196. re->reType = RT_RAIL_CORE;
  197. re->customShader = cgs.media.railCoreShader;
  198. VectorCopy(start, re->origin);
  199. VectorCopy(end, re->oldorigin);
  200. re->shaderRGBA[0] = ci->color1[0] * 255;
  201. re->shaderRGBA[1] = ci->color1[1] * 255;
  202. re->shaderRGBA[2] = ci->color1[2] * 255;
  203. re->shaderRGBA[3] = 255;
  204. le->color[0] = ci->color1[0] * 0.75;
  205. le->color[1] = ci->color1[1] * 0.75;
  206. le->color[2] = ci->color1[2] * 0.75;
  207. le->color[3] = 1.0f;
  208. AxisClear( re->axis );
  209. VectorMA(move, 20, vec, move);
  210. VectorCopy(move, next_move);
  211. VectorScale (vec, SPACING, vec);
  212. if (cg_oldRail.integer != 0) {
  213. // nudge down a bit so it isn't exactly in center
  214. re->origin[2] -= 8;
  215. re->oldorigin[2] -= 8;
  216. return;
  217. }
  218. skip = -1;
  219. j = 18;
  220. for (i = 0; i < len; i += SPACING) {
  221. if (i != skip) {
  222. skip = i + SPACING;
  223. le = CG_AllocLocalEntity();
  224. re = &le->refEntity;
  225. le->leFlags = LEF_PUFF_DONT_SCALE;
  226. le->leType = LE_MOVE_SCALE_FADE;
  227. le->startTime = cg.time;
  228. le->endTime = cg.time + (i>>1) + 600;
  229. le->lifeRate = 1.0 / (le->endTime - le->startTime);
  230. re->shaderTime = cg.time / 1000.0f;
  231. re->reType = RT_SPRITE;
  232. re->radius = 1.1f;
  233. re->customShader = cgs.media.railRingsShader;
  234. re->shaderRGBA[0] = ci->color2[0] * 255;
  235. re->shaderRGBA[1] = ci->color2[1] * 255;
  236. re->shaderRGBA[2] = ci->color2[2] * 255;
  237. re->shaderRGBA[3] = 255;
  238. le->color[0] = ci->color2[0] * 0.75;
  239. le->color[1] = ci->color2[1] * 0.75;
  240. le->color[2] = ci->color2[2] * 0.75;
  241. le->color[3] = 1.0f;
  242. le->pos.trType = TR_LINEAR;
  243. le->pos.trTime = cg.time;
  244. VectorCopy( move, move2);
  245. VectorMA(move2, RADIUS , axis[j], move2);
  246. VectorCopy(move2, le->pos.trBase);
  247. le->pos.trDelta[0] = axis[j][0]*6;
  248. le->pos.trDelta[1] = axis[j][1]*6;
  249. le->pos.trDelta[2] = axis[j][2]*6;
  250. }
  251. VectorAdd (move, vec, move);
  252. j = j + ROTATION < 36 ? j + ROTATION : (j + ROTATION) % 36;
  253. }
  254. }
  255. /*
  256. ==========================
  257. CG_RocketTrail
  258. ==========================
  259. */
  260. static void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) {
  261. int step;
  262. vec3_t origin, lastPos;
  263. int t;
  264. int startTime, contents;
  265. int lastContents;
  266. entityState_t *es;
  267. vec3_t up;
  268. localEntity_t *smoke;
  269. if ( cg_noProjectileTrail.integer ) {
  270. return;
  271. }
  272. up[0] = 0;
  273. up[1] = 0;
  274. up[2] = 0;
  275. step = 50;
  276. es = &ent->currentState;
  277. startTime = ent->trailTime;
  278. t = step * ( (startTime + step) / step );
  279. BG_EvaluateTrajectory( &es->pos, cg.time, origin );
  280. contents = CG_PointContents( origin, -1 );
  281. // if object (e.g. grenade) is stationary, don't toss up smoke
  282. if ( es->pos.trType == TR_STATIONARY ) {
  283. ent->trailTime = cg.time;
  284. return;
  285. }
  286. BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
  287. lastContents = CG_PointContents( lastPos, -1 );
  288. ent->trailTime = cg.time;
  289. if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
  290. if ( contents & lastContents & CONTENTS_WATER ) {
  291. CG_BubbleTrail( lastPos, origin, 8 );
  292. }
  293. return;
  294. }
  295. for ( ; t <= ent->trailTime ; t += step ) {
  296. BG_EvaluateTrajectory( &es->pos, t, lastPos );
  297. smoke = CG_SmokePuff( lastPos, up,
  298. wi->trailRadius,
  299. 1, 1, 1, 0.33f,
  300. wi->wiTrailTime,
  301. t,
  302. 0,
  303. 0,
  304. cgs.media.smokePuffShader );
  305. // use the optimized local entity add
  306. smoke->leType = LE_SCALE_FADE;
  307. }
  308. }
  309. #ifdef MISSIONPACK
  310. /*
  311. ==========================
  312. CG_NailTrail
  313. ==========================
  314. */
  315. static void CG_NailTrail( centity_t *ent, const weaponInfo_t *wi ) {
  316. int step;
  317. vec3_t origin, lastPos;
  318. int t;
  319. int startTime, contents;
  320. int lastContents;
  321. entityState_t *es;
  322. vec3_t up;
  323. localEntity_t *smoke;
  324. if ( cg_noProjectileTrail.integer ) {
  325. return;
  326. }
  327. up[0] = 0;
  328. up[1] = 0;
  329. up[2] = 0;
  330. step = 50;
  331. es = &ent->currentState;
  332. startTime = ent->trailTime;
  333. t = step * ( (startTime + step) / step );
  334. BG_EvaluateTrajectory( &es->pos, cg.time, origin );
  335. contents = CG_PointContents( origin, -1 );
  336. // if object (e.g. grenade) is stationary, don't toss up smoke
  337. if ( es->pos.trType == TR_STATIONARY ) {
  338. ent->trailTime = cg.time;
  339. return;
  340. }
  341. BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
  342. lastContents = CG_PointContents( lastPos, -1 );
  343. ent->trailTime = cg.time;
  344. if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
  345. if ( contents & lastContents & CONTENTS_WATER ) {
  346. CG_BubbleTrail( lastPos, origin, 8 );
  347. }
  348. return;
  349. }
  350. for ( ; t <= ent->trailTime ; t += step ) {
  351. BG_EvaluateTrajectory( &es->pos, t, lastPos );
  352. smoke = CG_SmokePuff( lastPos, up,
  353. wi->trailRadius,
  354. 1, 1, 1, 0.33f,
  355. wi->wiTrailTime,
  356. t,
  357. 0,
  358. 0,
  359. cgs.media.nailPuffShader );
  360. // use the optimized local entity add
  361. smoke->leType = LE_SCALE_FADE;
  362. }
  363. }
  364. #endif
  365. /*
  366. ==========================
  367. CG_NailTrail
  368. ==========================
  369. */
  370. static void CG_PlasmaTrail( centity_t *cent, const weaponInfo_t *wi ) {
  371. localEntity_t *le;
  372. refEntity_t *re;
  373. entityState_t *es;
  374. vec3_t velocity, xvelocity, origin;
  375. vec3_t offset, xoffset;
  376. vec3_t v[3];
  377. int t, startTime, step;
  378. float waterScale = 1.0f;
  379. if ( cg_noProjectileTrail.integer || cg_oldPlasma.integer ) {
  380. return;
  381. }
  382. step = 50;
  383. es = &cent->currentState;
  384. startTime = cent->trailTime;
  385. t = step * ( (startTime + step) / step );
  386. BG_EvaluateTrajectory( &es->pos, cg.time, origin );
  387. le = CG_AllocLocalEntity();
  388. re = &le->refEntity;
  389. velocity[0] = 60 - 120 * crandom();
  390. velocity[1] = 40 - 80 * crandom();
  391. velocity[2] = 100 - 200 * crandom();
  392. le->leType = LE_MOVE_SCALE_FADE;
  393. le->leFlags = LEF_TUMBLE;
  394. le->leBounceSoundType = LEBS_NONE;
  395. le->leMarkType = LEMT_NONE;
  396. le->startTime = cg.time;
  397. le->endTime = le->startTime + 600;
  398. le->pos.trType = TR_GRAVITY;
  399. le->pos.trTime = cg.time;
  400. AnglesToAxis( cent->lerpAngles, v );
  401. offset[0] = 2;
  402. offset[1] = 2;
  403. offset[2] = 2;
  404. xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
  405. xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
  406. xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
  407. VectorAdd( origin, xoffset, re->origin );
  408. VectorCopy( re->origin, le->pos.trBase );
  409. if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
  410. waterScale = 0.10f;
  411. }
  412. xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
  413. xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
  414. xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
  415. VectorScale( xvelocity, waterScale, le->pos.trDelta );
  416. AxisCopy( axisDefault, re->axis );
  417. re->shaderTime = cg.time / 1000.0f;
  418. re->reType = RT_SPRITE;
  419. re->radius = 0.25f;
  420. re->customShader = cgs.media.railRingsShader;
  421. le->bounceFactor = 0.3f;
  422. re->shaderRGBA[0] = wi->flashDlightColor[0] * 63;
  423. re->shaderRGBA[1] = wi->flashDlightColor[1] * 63;
  424. re->shaderRGBA[2] = wi->flashDlightColor[2] * 63;
  425. re->shaderRGBA[3] = 63;
  426. le->color[0] = wi->flashDlightColor[0] * 0.2;
  427. le->color[1] = wi->flashDlightColor[1] * 0.2;
  428. le->color[2] = wi->flashDlightColor[2] * 0.2;
  429. le->color[3] = 0.25f;
  430. le->angles.trType = TR_LINEAR;
  431. le->angles.trTime = cg.time;
  432. le->angles.trBase[0] = rand()&31;
  433. le->angles.trBase[1] = rand()&31;
  434. le->angles.trBase[2] = rand()&31;
  435. le->angles.trDelta[0] = 1;
  436. le->angles.trDelta[1] = 0.5;
  437. le->angles.trDelta[2] = 0;
  438. }
  439. /*
  440. ==========================
  441. CG_GrappleTrail
  442. ==========================
  443. */
  444. void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ) {
  445. vec3_t origin;
  446. entityState_t *es;
  447. vec3_t forward, up;
  448. refEntity_t beam;
  449. es = &ent->currentState;
  450. BG_EvaluateTrajectory( &es->pos, cg.time, origin );
  451. ent->trailTime = cg.time;
  452. memset( &beam, 0, sizeof( beam ) );
  453. //FIXME adjust for muzzle position
  454. VectorCopy ( cg_entities[ ent->currentState.otherEntityNum ].lerpOrigin, beam.origin );
  455. beam.origin[2] += 26;
  456. AngleVectors( cg_entities[ ent->currentState.otherEntityNum ].lerpAngles, forward, NULL, up );
  457. VectorMA( beam.origin, -6, up, beam.origin );
  458. VectorCopy( origin, beam.oldorigin );
  459. if (Distance( beam.origin, beam.oldorigin ) < 64 )
  460. return; // Don't draw if close
  461. beam.reType = RT_LIGHTNING;
  462. beam.customShader = cgs.media.lightningShader;
  463. AxisClear( beam.axis );
  464. beam.shaderRGBA[0] = 0xff;
  465. beam.shaderRGBA[1] = 0xff;
  466. beam.shaderRGBA[2] = 0xff;
  467. beam.shaderRGBA[3] = 0xff;
  468. trap_R_AddRefEntityToScene( &beam );
  469. }
  470. /*
  471. ==========================
  472. CG_GrenadeTrail
  473. ==========================
  474. */
  475. static void CG_GrenadeTrail( centity_t *ent, const weaponInfo_t *wi ) {
  476. CG_RocketTrail( ent, wi );
  477. }
  478. /*
  479. =================
  480. CG_RegisterWeapon
  481. The server says this item is used on this level
  482. =================
  483. */
  484. void CG_RegisterWeapon( int weaponNum ) {
  485. weaponInfo_t *weaponInfo;
  486. gitem_t *item, *ammo;
  487. char path[MAX_QPATH];
  488. vec3_t mins, maxs;
  489. int i;
  490. weaponInfo = &cg_weapons[weaponNum];
  491. if ( weaponNum == 0 ) {
  492. return;
  493. }
  494. if ( weaponInfo->registered ) {
  495. return;
  496. }
  497. memset( weaponInfo, 0, sizeof( *weaponInfo ) );
  498. weaponInfo->registered = qtrue;
  499. for ( item = bg_itemlist + 1 ; item->classname ; item++ ) {
  500. if ( item->giType == IT_WEAPON && item->giTag == weaponNum ) {
  501. weaponInfo->item = item;
  502. break;
  503. }
  504. }
  505. if ( !item->classname ) {
  506. CG_Error( "Couldn't find weapon %i", weaponNum );
  507. }
  508. CG_RegisterItemVisuals( item - bg_itemlist );
  509. // load cmodel before model so filecache works
  510. weaponInfo->weaponModel = trap_R_RegisterModel( item->world_model[0] );
  511. // calc midpoint for rotation
  512. trap_R_ModelBounds( weaponInfo->weaponModel, mins, maxs );
  513. for ( i = 0 ; i < 3 ; i++ ) {
  514. weaponInfo->weaponMidpoint[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] );
  515. }
  516. weaponInfo->weaponIcon = trap_R_RegisterShader( item->icon );
  517. weaponInfo->ammoIcon = trap_R_RegisterShader( item->icon );
  518. for ( ammo = bg_itemlist + 1 ; ammo->classname ; ammo++ ) {
  519. if ( ammo->giType == IT_AMMO && ammo->giTag == weaponNum ) {
  520. break;
  521. }
  522. }
  523. if ( ammo->classname && ammo->world_model[0] ) {
  524. weaponInfo->ammoModel = trap_R_RegisterModel( ammo->world_model[0] );
  525. }
  526. strcpy( path, item->world_model[0] );
  527. COM_StripExtension( path, path );
  528. strcat( path, "_flash.md3" );
  529. weaponInfo->flashModel = trap_R_RegisterModel( path );
  530. strcpy( path, item->world_model[0] );
  531. COM_StripExtension( path, path );
  532. strcat( path, "_barrel.md3" );
  533. weaponInfo->barrelModel = trap_R_RegisterModel( path );
  534. strcpy( path, item->world_model[0] );
  535. COM_StripExtension( path, path );
  536. strcat( path, "_hand.md3" );
  537. weaponInfo->handsModel = trap_R_RegisterModel( path );
  538. if ( !weaponInfo->handsModel ) {
  539. weaponInfo->handsModel = trap_R_RegisterModel( "models/weapons2/shotgun/shotgun_hand.md3" );
  540. }
  541. weaponInfo->loopFireSound = qfalse;
  542. switch ( weaponNum ) {
  543. case WP_GAUNTLET:
  544. MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
  545. weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse );
  546. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/melee/fstatck.wav", qfalse );
  547. break;
  548. case WP_LIGHTNING:
  549. MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
  550. weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse );
  551. weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/lightning/lg_hum.wav", qfalse );
  552. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/lightning/lg_fire.wav", qfalse );
  553. cgs.media.lightningShader = trap_R_RegisterShader( "lightningBoltNew");
  554. cgs.media.lightningExplosionModel = trap_R_RegisterModel( "models/weaphits/crackle.md3" );
  555. cgs.media.sfx_lghit1 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit.wav", qfalse );
  556. cgs.media.sfx_lghit2 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit2.wav", qfalse );
  557. cgs.media.sfx_lghit3 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit3.wav", qfalse );
  558. break;
  559. case WP_GRAPPLING_HOOK:
  560. MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
  561. weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" );
  562. weaponInfo->missileTrailFunc = CG_GrappleTrail;
  563. weaponInfo->missileDlight = 200;
  564. weaponInfo->wiTrailTime = 2000;
  565. weaponInfo->trailRadius = 64;
  566. MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 );
  567. weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse );
  568. weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse );
  569. break;
  570. #ifdef MISSIONPACK
  571. case WP_CHAINGUN:
  572. weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/vulcan/wvulfire.wav", qfalse );
  573. weaponInfo->loopFireSound = qtrue;
  574. MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 );
  575. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf1b.wav", qfalse );
  576. weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf2b.wav", qfalse );
  577. weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf3b.wav", qfalse );
  578. weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf4b.wav", qfalse );
  579. weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
  580. cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" );
  581. break;
  582. #endif
  583. case WP_MACHINEGUN:
  584. MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 );
  585. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf1b.wav", qfalse );
  586. weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf2b.wav", qfalse );
  587. weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf3b.wav", qfalse );
  588. weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf4b.wav", qfalse );
  589. weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
  590. cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" );
  591. break;
  592. case WP_SHOTGUN:
  593. MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 );
  594. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/shotgun/sshotf1b.wav", qfalse );
  595. weaponInfo->ejectBrassFunc = CG_ShotgunEjectBrass;
  596. break;
  597. case WP_ROCKET_LAUNCHER:
  598. weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" );
  599. weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse );
  600. weaponInfo->missileTrailFunc = CG_RocketTrail;
  601. weaponInfo->missileDlight = 200;
  602. weaponInfo->wiTrailTime = 2000;
  603. weaponInfo->trailRadius = 64;
  604. MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 );
  605. MAKERGB( weaponInfo->flashDlightColor, 1, 0.75f, 0 );
  606. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav", qfalse );
  607. cgs.media.rocketExplosionShader = trap_R_RegisterShader( "rocketExplosion" );
  608. break;
  609. #ifdef MISSIONPACK
  610. case WP_PROX_LAUNCHER:
  611. weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/proxmine.md3" );
  612. weaponInfo->missileTrailFunc = CG_GrenadeTrail;
  613. weaponInfo->wiTrailTime = 700;
  614. weaponInfo->trailRadius = 32;
  615. MAKERGB( weaponInfo->flashDlightColor, 1, 0.70f, 0 );
  616. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/proxmine/wstbfire.wav", qfalse );
  617. cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" );
  618. break;
  619. #endif
  620. case WP_GRENADE_LAUNCHER:
  621. weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/grenade1.md3" );
  622. weaponInfo->missileTrailFunc = CG_GrenadeTrail;
  623. weaponInfo->wiTrailTime = 700;
  624. weaponInfo->trailRadius = 32;
  625. MAKERGB( weaponInfo->flashDlightColor, 1, 0.70f, 0 );
  626. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/grenade/grenlf1a.wav", qfalse );
  627. cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" );
  628. break;
  629. #ifdef MISSIONPACK
  630. case WP_NAILGUN:
  631. weaponInfo->ejectBrassFunc = CG_NailgunEjectBrass;
  632. weaponInfo->missileTrailFunc = CG_NailTrail;
  633. // weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/nailgun/wnalflit.wav", qfalse );
  634. weaponInfo->trailRadius = 16;
  635. weaponInfo->wiTrailTime = 250;
  636. weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/nail.md3" );
  637. MAKERGB( weaponInfo->flashDlightColor, 1, 0.75f, 0 );
  638. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/nailgun/wnalfire.wav", qfalse );
  639. break;
  640. #endif
  641. case WP_PLASMAGUN:
  642. // weaponInfo->missileModel = cgs.media.invulnerabilityPowerupModel;
  643. weaponInfo->missileTrailFunc = CG_PlasmaTrail;
  644. weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/plasma/lasfly.wav", qfalse );
  645. MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
  646. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/plasma/hyprbf1a.wav", qfalse );
  647. cgs.media.plasmaExplosionShader = trap_R_RegisterShader( "plasmaExplosion" );
  648. cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" );
  649. break;
  650. case WP_RAILGUN:
  651. weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/railgun/rg_hum.wav", qfalse );
  652. MAKERGB( weaponInfo->flashDlightColor, 1, 0.5f, 0 );
  653. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/railgun/railgf1a.wav", qfalse );
  654. cgs.media.railExplosionShader = trap_R_RegisterShader( "railExplosion" );
  655. cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" );
  656. cgs.media.railCoreShader = trap_R_RegisterShader( "railCore" );
  657. break;
  658. case WP_BFG:
  659. weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/bfg/bfg_hum.wav", qfalse );
  660. MAKERGB( weaponInfo->flashDlightColor, 1, 0.7f, 1 );
  661. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/bfg/bfg_fire.wav", qfalse );
  662. cgs.media.bfgExplosionShader = trap_R_RegisterShader( "bfgExplosion" );
  663. weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/bfg.md3" );
  664. weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse );
  665. break;
  666. default:
  667. MAKERGB( weaponInfo->flashDlightColor, 1, 1, 1 );
  668. weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav", qfalse );
  669. break;
  670. }
  671. }
  672. /*
  673. =================
  674. CG_RegisterItemVisuals
  675. The server says this item is used on this level
  676. =================
  677. */
  678. void CG_RegisterItemVisuals( int itemNum ) {
  679. itemInfo_t *itemInfo;
  680. gitem_t *item;
  681. if ( itemNum < 0 || itemNum >= bg_numItems ) {
  682. CG_Error( "CG_RegisterItemVisuals: itemNum %d out of range [0-%d]", itemNum, bg_numItems-1 );
  683. }
  684. itemInfo = &cg_items[ itemNum ];
  685. if ( itemInfo->registered ) {
  686. return;
  687. }
  688. item = &bg_itemlist[ itemNum ];
  689. memset( itemInfo, 0, sizeof( &itemInfo ) );
  690. itemInfo->registered = qtrue;
  691. itemInfo->models[0] = trap_R_RegisterModel( item->world_model[0] );
  692. itemInfo->icon = trap_R_RegisterShader( item->icon );
  693. if ( item->giType == IT_WEAPON ) {
  694. CG_RegisterWeapon( item->giTag );
  695. }
  696. //
  697. // powerups have an accompanying ring or sphere
  698. //
  699. if ( item->giType == IT_POWERUP || item->giType == IT_HEALTH ||
  700. item->giType == IT_ARMOR || item->giType == IT_HOLDABLE ) {
  701. if ( item->world_model[1] ) {
  702. itemInfo->models[1] = trap_R_RegisterModel( item->world_model[1] );
  703. }
  704. }
  705. }
  706. /*
  707. ========================================================================================
  708. VIEW WEAPON
  709. ========================================================================================
  710. */
  711. /*
  712. =================
  713. CG_MapTorsoToWeaponFrame
  714. =================
  715. */
  716. static int CG_MapTorsoToWeaponFrame( clientInfo_t *ci, int frame ) {
  717. // change weapon
  718. if ( frame >= ci->animations[TORSO_DROP].firstFrame
  719. && frame < ci->animations[TORSO_DROP].firstFrame + 9 ) {
  720. return frame - ci->animations[TORSO_DROP].firstFrame + 6;
  721. }
  722. // stand attack
  723. if ( frame >= ci->animations[TORSO_ATTACK].firstFrame
  724. && frame < ci->animations[TORSO_ATTACK].firstFrame + 6 ) {
  725. return 1 + frame - ci->animations[TORSO_ATTACK].firstFrame;
  726. }
  727. // stand attack 2
  728. if ( frame >= ci->animations[TORSO_ATTACK2].firstFrame
  729. && frame < ci->animations[TORSO_ATTACK2].firstFrame + 6 ) {
  730. return 1 + frame - ci->animations[TORSO_ATTACK2].firstFrame;
  731. }
  732. return 0;
  733. }
  734. /*
  735. ==============
  736. CG_CalculateWeaponPosition
  737. ==============
  738. */
  739. static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) {
  740. float scale;
  741. int delta;
  742. float fracsin;
  743. VectorCopy( cg.refdef.vieworg, origin );
  744. VectorCopy( cg.refdefViewAngles, angles );
  745. // on odd legs, invert some angles
  746. if ( cg.bobcycle & 1 ) {
  747. scale = -cg.xyspeed;
  748. } else {
  749. scale = cg.xyspeed;
  750. }
  751. // gun angles from bobbing
  752. angles[ROLL] += scale * cg.bobfracsin * 0.005;
  753. angles[YAW] += scale * cg.bobfracsin * 0.01;
  754. angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005;
  755. // drop the weapon when landing
  756. delta = cg.time - cg.landTime;
  757. if ( delta < LAND_DEFLECT_TIME ) {
  758. origin[2] += cg.landChange*0.25 * delta / LAND_DEFLECT_TIME;
  759. } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
  760. origin[2] += cg.landChange*0.25 *
  761. (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME;
  762. }
  763. #if 0
  764. // drop the weapon when stair climbing
  765. delta = cg.time - cg.stepTime;
  766. if ( delta < STEP_TIME/2 ) {
  767. origin[2] -= cg.stepChange*0.25 * delta / (STEP_TIME/2);
  768. } else if ( delta < STEP_TIME ) {
  769. origin[2] -= cg.stepChange*0.25 * (STEP_TIME - delta) / (STEP_TIME/2);
  770. }
  771. #endif
  772. // idle drift
  773. scale = cg.xyspeed + 40;
  774. fracsin = sin( cg.time * 0.001 );
  775. angles[ROLL] += scale * fracsin * 0.01;
  776. angles[YAW] += scale * fracsin * 0.01;
  777. angles[PITCH] += scale * fracsin * 0.01;
  778. }
  779. /*
  780. ===============
  781. CG_LightningBolt
  782. Origin will be the exact tag point, which is slightly
  783. different than the muzzle point used for determining hits.
  784. The cent should be the non-predicted cent if it is from the player,
  785. so the endpoint will reflect the simulated strike (lagging the predicted
  786. angle)
  787. ===============
  788. */
  789. static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
  790. trace_t trace;
  791. refEntity_t beam;
  792. vec3_t forward;
  793. vec3_t muzzlePoint, endPoint;
  794. if (cent->currentState.weapon != WP_LIGHTNING) {
  795. return;
  796. }
  797. memset( &beam, 0, sizeof( beam ) );
  798. // CPMA "true" lightning
  799. if ((cent->currentState.number == cg.predictedPlayerState.clientNum) && (cg_trueLightning.value != 0)) {
  800. vec3_t angle;
  801. int i;
  802. for (i = 0; i < 3; i++) {
  803. float a = cent->lerpAngles[i] - cg.refdefViewAngles[i];
  804. if (a > 180) {
  805. a -= 360;
  806. }
  807. if (a < -180) {
  808. a += 360;
  809. }
  810. angle[i] = cg.refdefViewAngles[i] + a * (1.0 - cg_trueLightning.value);
  811. if (angle[i] < 0) {
  812. angle[i] += 360;
  813. }
  814. if (angle[i] > 360) {
  815. angle[i] -= 360;
  816. }
  817. }
  818. AngleVectors(angle, forward, NULL, NULL );
  819. VectorCopy(cent->lerpOrigin, muzzlePoint );
  820. // VectorCopy(cg.refdef.vieworg, muzzlePoint );
  821. } else {
  822. // !CPMA
  823. AngleVectors( cent->lerpAngles, forward, NULL, NULL );
  824. VectorCopy(cent->lerpOrigin, muzzlePoint );
  825. }
  826. // FIXME: crouch
  827. muzzlePoint[2] += DEFAULT_VIEWHEIGHT;
  828. VectorMA( muzzlePoint, 14, forward, muzzlePoint );
  829. // project forward by the lightning range
  830. VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint );
  831. // see if it hit a wall
  832. CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint,
  833. cent->currentState.number, MASK_SHOT );
  834. // this is the endpoint
  835. VectorCopy( trace.endpos, beam.oldorigin );
  836. // use the provided origin, even though it may be slightly
  837. // different than the muzzle origin
  838. VectorCopy( origin, beam.origin );
  839. beam.reType = RT_LIGHTNING;
  840. beam.customShader = cgs.media.lightningShader;
  841. trap_R_AddRefEntityToScene( &beam );
  842. // add the impact flare if it hit something
  843. if ( trace.fraction < 1.0 ) {
  844. vec3_t angles;
  845. vec3_t dir;
  846. VectorSubtract( beam.oldorigin, beam.origin, dir );
  847. VectorNormalize( dir );
  848. memset( &beam, 0, sizeof( beam ) );
  849. beam.hModel = cgs.media.lightningExplosionModel;
  850. VectorMA( trace.endpos, -16, dir, beam.origin );
  851. // make a random orientation
  852. angles[0] = rand() % 360;
  853. angles[1] = rand() % 360;
  854. angles[2] = rand() % 360;
  855. AnglesToAxis( angles, beam.axis );
  856. trap_R_AddRefEntityToScene( &beam );
  857. }
  858. }
  859. /*
  860. static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
  861. trace_t trace;
  862. refEntity_t beam;
  863. vec3_t forward;
  864. vec3_t muzzlePoint, endPoint;
  865. if ( cent->currentState.weapon != WP_LIGHTNING ) {
  866. return;
  867. }
  868. memset( &beam, 0, sizeof( beam ) );
  869. // find muzzle point for this frame
  870. VectorCopy( cent->lerpOrigin, muzzlePoint );
  871. AngleVectors( cent->lerpAngles, forward, NULL, NULL );
  872. // FIXME: crouch
  873. muzzlePoint[2] += DEFAULT_VIEWHEIGHT;
  874. VectorMA( muzzlePoint, 14, forward, muzzlePoint );
  875. // project forward by the lightning range
  876. VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint );
  877. // see if it hit a wall
  878. CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint,
  879. cent->currentState.number, MASK_SHOT );
  880. // this is the endpoint
  881. VectorCopy( trace.endpos, beam.oldorigin );
  882. // use the provided origin, even though it may be slightly
  883. // different than the muzzle origin
  884. VectorCopy( origin, beam.origin );
  885. beam.reType = RT_LIGHTNING;
  886. beam.customShader = cgs.media.lightningShader;
  887. trap_R_AddRefEntityToScene( &beam );
  888. // add the impact flare if it hit something
  889. if ( trace.fraction < 1.0 ) {
  890. vec3_t angles;
  891. vec3_t dir;
  892. VectorSubtract( beam.oldorigin, beam.origin, dir );
  893. VectorNormalize( dir );
  894. memset( &beam, 0, sizeof( beam ) );
  895. beam.hModel = cgs.media.lightningExplosionModel;
  896. VectorMA( trace.endpos, -16, dir, beam.origin );
  897. // make a random orientation
  898. angles[0] = rand() % 360;
  899. angles[1] = rand() % 360;
  900. angles[2] = rand() % 360;
  901. AnglesToAxis( angles, beam.axis );
  902. trap_R_AddRefEntityToScene( &beam );
  903. }
  904. }
  905. */
  906. /*
  907. ===============
  908. CG_SpawnRailTrail
  909. Origin will be the exact tag point, which is slightly
  910. different than the muzzle point used for determining hits.
  911. ===============
  912. */
  913. static void CG_SpawnRailTrail( centity_t *cent, vec3_t origin ) {
  914. clientInfo_t *ci;
  915. if ( cent->currentState.weapon != WP_RAILGUN ) {
  916. return;
  917. }
  918. if ( !cent->pe.railgunFlash ) {
  919. return;
  920. }
  921. cent->pe.railgunFlash = qtrue;
  922. ci = &cgs.clientinfo[ cent->currentState.clientNum ];
  923. CG_RailTrail( ci, origin, cent->pe.railgunImpact );
  924. }
  925. /*
  926. ======================
  927. CG_MachinegunSpinAngle
  928. ======================
  929. */
  930. #define SPIN_SPEED 0.9
  931. #define COAST_TIME 1000
  932. static float CG_MachinegunSpinAngle( centity_t *cent ) {
  933. int delta;
  934. float angle;
  935. float speed;
  936. delta = cg.time - cent->pe.barrelTime;
  937. if ( cent->pe.barrelSpinning ) {
  938. angle = cent->pe.barrelAngle + delta * SPIN_SPEED;
  939. } else {
  940. if ( delta > COAST_TIME ) {
  941. delta = COAST_TIME;
  942. }
  943. speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
  944. angle = cent->pe.barrelAngle + delta * speed;
  945. }
  946. if ( cent->pe.barrelSpinning == !(cent->currentState.eFlags & EF_FIRING) ) {
  947. cent->pe.barrelTime = cg.time;
  948. cent->pe.barrelAngle = AngleMod( angle );
  949. cent->pe.barrelSpinning = !!(cent->currentState.eFlags & EF_FIRING);
  950. #ifdef MISSIONPACK
  951. if ( cent->currentState.weapon == WP_CHAINGUN && !cent->pe.barrelSpinning ) {
  952. trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, trap_S_RegisterSound( "sound/weapons/vulcan/wvulwind.wav", qfalse ) );
  953. }
  954. #endif
  955. }
  956. return angle;
  957. }
  958. /*
  959. ========================
  960. CG_AddWeaponWithPowerups
  961. ========================
  962. */
  963. static void CG_AddWeaponWithPowerups( refEntity_t *gun, int powerups ) {
  964. // add powerup effects
  965. if ( powerups & ( 1 << PW_INVIS ) ) {
  966. gun->customShader = cgs.media.invisShader;
  967. trap_R_AddRefEntityToScene( gun );
  968. } else {
  969. trap_R_AddRefEntityToScene( gun );
  970. if ( powerups & ( 1 << PW_BATTLESUIT ) ) {
  971. gun->customShader = cgs.media.battleWeaponShader;
  972. trap_R_AddRefEntityToScene( gun );
  973. }
  974. if ( powerups & ( 1 << PW_QUAD ) ) {
  975. gun->customShader = cgs.media.quadWeaponShader;
  976. trap_R_AddRefEntityToScene( gun );
  977. }
  978. }
  979. }
  980. /*
  981. =============
  982. CG_AddPlayerWeapon
  983. Used for both the view weapon (ps is valid) and the world modelother character models (ps is NULL)
  984. The main player will have this called for BOTH cases, so effects like light and
  985. sound should only be done on the world model case.
  986. =============
  987. */
  988. void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team ) {
  989. refEntity_t gun;
  990. refEntity_t barrel;
  991. refEntity_t flash;
  992. vec3_t angles;
  993. weapon_t weaponNum;
  994. weaponInfo_t *weapon;
  995. centity_t *nonPredictedCent;
  996. // int col;
  997. weaponNum = cent->currentState.weapon;
  998. CG_RegisterWeapon( weaponNum );
  999. weapon = &cg_weapons[weaponNum];
  1000. // add the weapon
  1001. memset( &gun, 0, sizeof( gun ) );
  1002. VectorCopy( parent->lightingOrigin, gun.lightingOrigin );
  1003. gun.shadowPlane = parent->shadowPlane;
  1004. gun.renderfx = parent->renderfx;
  1005. // set custom shading for railgun refire rate
  1006. if ( ps ) {
  1007. if ( cg.predictedPlayerState.weapon == WP_RAILGUN
  1008. && cg.predictedPlayerState.weaponstate == WEAPON_FIRING ) {
  1009. float f;
  1010. f = (float)cg.predictedPlayerState.weaponTime / 1500;
  1011. gun.shaderRGBA[1] = 0;
  1012. gun.shaderRGBA[0] =
  1013. gun.shaderRGBA[2] = 255 * ( 1.0 - f );
  1014. } else {
  1015. gun.shaderRGBA[0] = 255;
  1016. gun.shaderRGBA[1] = 255;
  1017. gun.shaderRGBA[2] = 255;
  1018. gun.shaderRGBA[3] = 255;
  1019. }
  1020. }
  1021. gun.hModel = weapon->weaponModel;
  1022. if (!gun.hModel) {
  1023. return;
  1024. }
  1025. if ( !ps ) {
  1026. // add weapon ready sound
  1027. cent->pe.lightningFiring = qfalse;
  1028. if ( ( cent->currentState.eFlags & EF_FIRING ) && weapon->firingSound ) {
  1029. // lightning gun and guantlet make a different sound when fire is held down
  1030. trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->firingSound );
  1031. cent->pe.lightningFiring = qtrue;
  1032. } else if ( weapon->readySound ) {
  1033. trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->readySound );
  1034. }
  1035. }
  1036. CG_PositionEntityOnTag( &gun, parent, parent->hModel, "tag_weapon");
  1037. CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups );
  1038. // add the spinning barrel
  1039. if ( weapon->barrelModel ) {
  1040. memset( &barrel, 0, sizeof( barrel ) );
  1041. VectorCopy( parent->lightingOrigin, barrel.lightingOrigin );
  1042. barrel.shadowPlane = parent->shadowPlane;
  1043. barrel.renderfx = parent->renderfx;
  1044. barrel.hModel = weapon->barrelModel;
  1045. angles[YAW] = 0;
  1046. angles[PITCH] = 0;
  1047. angles[ROLL] = CG_MachinegunSpinAngle( cent );
  1048. AnglesToAxis( angles, barrel.axis );
  1049. CG_PositionRotatedEntityOnTag( &barrel, &gun, weapon->weaponModel, "tag_barrel" );
  1050. CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups );
  1051. }
  1052. // make sure we aren't looking at cg.predictedPlayerEntity for LG
  1053. nonPredictedCent = &cg_entities[cent->currentState.clientNum];
  1054. // if the index of the nonPredictedCent is not the same as the clientNum
  1055. // then this is a fake player (like on teh single player podiums), so
  1056. // go ahead and use the cent
  1057. if( ( nonPredictedCent - cg_entities ) != cent->currentState.clientNum ) {
  1058. nonPredictedCent = cent;
  1059. }
  1060. // add the flash
  1061. if ( ( weaponNum == WP_LIGHTNING || weaponNum == WP_GAUNTLET || weaponNum == WP_GRAPPLING_HOOK )
  1062. && ( nonPredictedCent->currentState.eFlags & EF_FIRING ) )
  1063. {
  1064. // continuous flash
  1065. } else {
  1066. // impulse flash
  1067. if ( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME && !cent->pe.railgunFlash ) {
  1068. return;
  1069. }
  1070. }
  1071. memset( &flash, 0, sizeof( flash ) );
  1072. VectorCopy( parent->lightingOrigin, flash.lightingOrigin );
  1073. flash.shadowPlane = parent->shadowPlane;
  1074. flash.renderfx = parent->renderfx;
  1075. flash.hModel = weapon->flashModel;
  1076. if (!flash.hModel) {
  1077. return;
  1078. }
  1079. angles[YAW] = 0;
  1080. angles[PITCH] = 0;
  1081. angles[ROLL] = crandom() * 10;
  1082. AnglesToAxis( angles, flash.axis );
  1083. // colorize the railgun blast
  1084. if ( weaponNum == WP_RAILGUN ) {
  1085. clientInfo_t *ci;
  1086. ci = &cgs.clientinfo[ cent->currentState.clientNum ];
  1087. flash.shaderRGBA[0] = 255 * ci->color1[0];
  1088. flash.shaderRGBA[1] = 255 * ci->color1[1];
  1089. flash.shaderRGBA[2] = 255 * ci->color1[2];
  1090. }
  1091. CG_PositionRotatedEntityOnTag( &flash, &gun, weapon->weaponModel, "tag_flash");
  1092. trap_R_AddRefEntityToScene( &flash );
  1093. if ( ps || cg.renderingThirdPerson ||
  1094. cent->currentState.number != cg.predictedPlayerState.clientNum ) {
  1095. // add lightning bolt
  1096. CG_LightningBolt( nonPredictedCent, flash.origin );
  1097. // add rail trail
  1098. CG_SpawnRailTrail( cent, flash.origin );
  1099. if ( weapon->flashDlightColor[0] || weapon->flashDlightColor[1] || weapon->flashDlightColor[2] ) {
  1100. trap_R_AddLightToScene( flash.origin, 300 + (rand()&31), weapon->flashDlightColor[0],
  1101. weapon->flashDlightColor[1], weapon->flashDlightColor[2] );
  1102. }
  1103. }
  1104. }
  1105. /*
  1106. ==============
  1107. CG_AddViewWeapon
  1108. Add the weapon, and flash for the player's view
  1109. ==============
  1110. */
  1111. void CG_AddViewWeapon( playerState_t *ps ) {
  1112. refEntity_t hand;
  1113. centity_t *cent;
  1114. clientInfo_t *ci;
  1115. float fovOffset;
  1116. vec3_t angles;
  1117. weaponInfo_t *weapon;
  1118. if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
  1119. return;
  1120. }
  1121. if ( ps->pm_type == PM_INTERMISSION ) {
  1122. return;
  1123. }
  1124. // no gun if in third person view or a camera is active
  1125. //if ( cg.renderingThirdPerson || cg.cameraMode) {
  1126. if ( cg.renderingThirdPerson ) {
  1127. return;
  1128. }
  1129. // allow the gun to be completely removed
  1130. if ( !cg_drawGun.integer ) {
  1131. vec3_t origin;
  1132. if ( cg.predictedPlayerState.eFlags & EF_FIRING ) {
  1133. // special hack for lightning gun...
  1134. VectorCopy( cg.refdef.vieworg, origin );
  1135. VectorMA( origin, -8, cg.refdef.viewaxis[2], origin );
  1136. CG_LightningBolt( &cg_entities[ps->clientNum], origin );
  1137. }
  1138. return;
  1139. }
  1140. // don't draw if testing a gun model
  1141. if ( cg.testGun ) {
  1142. return;
  1143. }
  1144. // drop gun lower at higher fov
  1145. if ( cg_fov.integer > 90 ) {
  1146. fovOffset = -0.2 * ( cg_fov.integer - 90 );
  1147. } else {
  1148. fovOffset = 0;
  1149. }
  1150. cent = &cg.predictedPlayerEntity; // &cg_entities[cg.snap->ps.clientNum];
  1151. CG_RegisterWeapon( ps->weapon );
  1152. weapon = &cg_weapons[ ps->weapon ];
  1153. memset (&hand, 0, sizeof(hand));
  1154. // set up gun position
  1155. CG_CalculateWeaponPosition( hand.origin, angles );
  1156. VectorMA( hand.origin, cg_gun_x.value, cg.refdef.viewaxis[0], hand.origin );
  1157. VectorMA( hand.origin, cg_gun_y.value, cg.refdef.viewaxis[1], hand.origin );
  1158. VectorMA( hand.origin, (cg_gun_z.value+fovOffset), cg.refdef.viewaxis[2], hand.origin );
  1159. AnglesToAxis( angles, hand.axis );
  1160. // map torso animations to weapon animations
  1161. if ( cg_gun_frame.integer ) {
  1162. // development tool
  1163. hand.frame = hand.oldframe = cg_gun_frame.integer;
  1164. hand.backlerp = 0;
  1165. } else {
  1166. // get clientinfo for animation map
  1167. ci = &cgs.clientinfo[ cent->currentState.clientNum ];
  1168. hand.frame = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.frame );
  1169. hand.oldframe = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.oldFrame );
  1170. hand.backlerp = cent->pe.torso.backlerp;
  1171. }
  1172. hand.hModel = weapon->handsModel;
  1173. hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_MINLIGHT;
  1174. // add everything onto the hand
  1175. CG_AddPlayerWeapon( &hand, ps, &cg.predictedPlayerEntity, ps->persistant[PERS_TEAM] );
  1176. }
  1177. /*
  1178. ==============================================================================
  1179. WEAPON SELECTION
  1180. ==============================================================================
  1181. */
  1182. /*
  1183. ===================
  1184. CG_DrawWeaponSelect
  1185. ===================
  1186. */
  1187. void CG_DrawWeaponSelect( void ) {
  1188. int i;
  1189. int bits;
  1190. int count;
  1191. int x, y, w;
  1192. char *name;
  1193. float *color;
  1194. // don't display if dead
  1195. if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
  1196. return;
  1197. }
  1198. color = CG_FadeColor( cg.weaponSelectTime, WEAPON_SELECT_TIME );
  1199. if ( !color ) {
  1200. return;
  1201. }
  1202. trap_R_SetColor( color );
  1203. // showing weapon select clears pickup item display, but not the blend blob
  1204. cg.itemPickupTime = 0;
  1205. // count the number of weapons owned
  1206. bits = cg.snap->ps.stats[ STAT_WEAPONS ];
  1207. count = 0;
  1208. for ( i = 1 ; i < 16 ; i++ ) {
  1209. if ( bits & ( 1 << i ) ) {
  1210. count++;
  1211. }
  1212. }
  1213. x = 320 - count * 20;
  1214. y = 380;
  1215. for ( i = 1 ; i < 16 ; i++ ) {
  1216. if ( !( bits & ( 1 << i ) ) ) {
  1217. continue;
  1218. }
  1219. CG_RegisterWeapon( i );
  1220. // draw weapon icon
  1221. CG_DrawPic( x, y, 32, 32, cg_weapons[i].weaponIcon );
  1222. // draw selection marker
  1223. if ( i == cg.weaponSelect ) {
  1224. CG_DrawPic( x-4, y-4, 40, 40, cgs.media.selectShader );
  1225. }
  1226. // no ammo cross on top
  1227. if ( !cg.snap->ps.ammo[ i ] ) {
  1228. CG_DrawPic( x, y, 32, 32, cgs.media.noammoShader );
  1229. }
  1230. x += 40;
  1231. }
  1232. // draw the selected name
  1233. if ( cg_weapons[ cg.weaponSelect ].item ) {
  1234. name = cg_weapons[ cg.weaponSelect ].item->pickup_name;
  1235. if ( name ) {
  1236. w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH;
  1237. x = ( SCREEN_WIDTH - w ) / 2;
  1238. CG_DrawBigStringColor(x, y - 22, name, color);
  1239. }
  1240. }
  1241. trap_R_SetColor( NULL );
  1242. }
  1243. /*
  1244. ===============
  1245. CG_WeaponSelectable
  1246. ===============
  1247. */
  1248. static qboolean CG_WeaponSelectable( int i ) {
  1249. if ( !cg.snap->ps.ammo[i] ) {
  1250. return qfalse;
  1251. }
  1252. if ( ! (cg.snap->ps.stats[ STAT_WEAPONS ] & ( 1 << i ) ) ) {
  1253. return qfalse;
  1254. }
  1255. return qtrue;
  1256. }
  1257. /*
  1258. ===============
  1259. CG_NextWeapon_f
  1260. ===============
  1261. */
  1262. void CG_NextWeapon_f( void ) {
  1263. int i;
  1264. int original;
  1265. if ( !cg.snap ) {
  1266. return;
  1267. }
  1268. if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
  1269. return;
  1270. }
  1271. cg.weaponSelectTime = cg.time;
  1272. original = cg.weaponSelect;
  1273. for ( i = 0 ; i < 16 ; i++ ) {
  1274. cg.weaponSelect++;
  1275. if ( cg.weaponSelect == 16 ) {
  1276. cg.weaponSelect = 0;
  1277. }
  1278. if ( cg.weaponSelect == WP_GAUNTLET ) {
  1279. continue; // never cycle to gauntlet
  1280. }
  1281. if ( CG_WeaponSelectable( cg.weaponSelect ) ) {
  1282. break;
  1283. }
  1284. }
  1285. if ( i == 16 ) {
  1286. cg.weaponSelect = original;
  1287. }
  1288. }
  1289. /*
  1290. ===============
  1291. CG_PrevWeapon_f
  1292. ===============
  1293. */
  1294. void CG_PrevWeapon_f( void ) {
  1295. int i;
  1296. int original;
  1297. if ( !cg.snap ) {
  1298. return;
  1299. }
  1300. if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
  1301. return;
  1302. }
  1303. cg.weaponSelectTime = cg.time;
  1304. original = cg.weaponSelect;
  1305. for ( i = 0 ; i < 16 ; i++ ) {
  1306. cg.weaponSelect--;
  1307. if ( cg.weaponSelect == -1 ) {
  1308. cg.weaponSelect = 15;
  1309. }
  1310. if ( cg.weaponSelect == WP_GAUNTLET ) {
  1311. continue; // never cycle to gauntlet
  1312. }
  1313. if ( CG_WeaponSelectable( cg.weaponSelect ) ) {
  1314. break;
  1315. }
  1316. }
  1317. if ( i == 16 ) {
  1318. cg.weaponSelect = original;
  1319. }
  1320. }
  1321. /*
  1322. ===============
  1323. CG_Weapon_f
  1324. ===============
  1325. */
  1326. void CG_Weapon_f( void ) {
  1327. int num;
  1328. if ( !cg.snap ) {
  1329. return;
  1330. }
  1331. if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
  1332. return;
  1333. }
  1334. num = atoi( CG_Argv( 1 ) );
  1335. if ( num < 1 || num > 15 ) {
  1336. return;
  1337. }
  1338. cg.weaponSelectTime = cg.time;
  1339. if ( ! ( cg.snap->ps.stats[STAT_WEAPONS] & ( 1 << num ) ) ) {
  1340. return; // don't have the weapon
  1341. }
  1342. cg.weaponSelect = num;
  1343. }
  1344. /*
  1345. ===================
  1346. CG_OutOfAmmoChange
  1347. The current weapon has just run out of ammo
  1348. ===================
  1349. */
  1350. void CG_OutOfAmmoChange( void ) {
  1351. int i;
  1352. cg.weaponSelectTime = cg.time;
  1353. for ( i = 15 ; i > 0 ; i-- ) {
  1354. if ( CG_WeaponSelectable( i ) ) {
  1355. cg.weaponSelect = i;
  1356. break;
  1357. }
  1358. }
  1359. }
  1360. /*
  1361. ===================================================================================================
  1362. WEAPON EVENTS
  1363. ===================================================================================================
  1364. */
  1365. /*
  1366. ================
  1367. CG_FireWeapon
  1368. Caused by an EV_FIRE_WEAPON event
  1369. ================
  1370. */
  1371. void CG_FireWeapon( centity_t *cent ) {
  1372. entityState_t *ent;
  1373. int c;
  1374. weaponInfo_t *weap;
  1375. ent = &cent->currentState;
  1376. if ( ent->weapon == WP_NONE ) {
  1377. return;
  1378. }
  1379. if ( ent->weapon >= WP_NUM_WEAPONS ) {
  1380. CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" );
  1381. return;
  1382. }
  1383. weap = &cg_weapons[ ent->weapon ];
  1384. // mark the entity as muzzle flashing, so when it is added it will
  1385. // append the flash to the weapon model
  1386. cent->muzzleFlashTime = cg.time;
  1387. // lightning gun only does this this on initial press
  1388. if ( ent->weapon == WP_LIGHTNING ) {
  1389. if ( cent->pe.lightningFiring ) {
  1390. return;
  1391. }
  1392. }
  1393. // play quad sound if needed
  1394. if ( cent->currentState.powerups & ( 1 << PW_QUAD ) ) {
  1395. trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.media.quadSound );
  1396. }
  1397. // play a sound
  1398. for ( c = 0 ; c < 4 ; c++ ) {
  1399. if ( !weap->flashSound[c] ) {
  1400. break;
  1401. }
  1402. }
  1403. if ( c > 0 ) {
  1404. c = rand() % c;
  1405. if ( weap->flashSound[c] )
  1406. {
  1407. trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, weap->flashSound[c] );
  1408. }
  1409. }
  1410. // do brass ejection
  1411. if ( weap->ejectBrassFunc && cg_brassTime.integer > 0 ) {
  1412. weap->ejectBrassFunc( cent );
  1413. }
  1414. }
  1415. /*
  1416. =================
  1417. CG_MissileHitWall
  1418. Caused by an EV_MISSILE_MISS event, or directly by local bullet tracing
  1419. =================
  1420. */
  1421. void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, impactSound_t soundType ) {
  1422. qhandle_t mod;
  1423. qhandle_t mark;
  1424. qhandle_t shader;
  1425. sfxHandle_t sfx;
  1426. float radius;
  1427. float light;
  1428. vec3_t lightColor;
  1429. localEntity_t *le;
  1430. int r;
  1431. qboolean alphaFade;
  1432. qboolean isSprite;
  1433. int duration;
  1434. vec3_t sprOrg;
  1435. vec3_t sprVel;
  1436. mark = 0;
  1437. radius = 32;
  1438. sfx = 0;
  1439. mod = 0;
  1440. shader = 0;
  1441. light = 0;
  1442. lightColor[0] = 1;
  1443. lightColor[1] = 1;
  1444. lightColor[2] = 0;
  1445. // set defaults
  1446. isSprite = qfalse;
  1447. duration = 600;
  1448. switch ( weapon ) {
  1449. default:
  1450. #ifdef MISSIONPACK
  1451. case WP_NAILGUN:
  1452. if( soundType == IMPACTSOUND_FLESH ) {
  1453. sfx = cgs.media.sfx_nghitflesh;
  1454. } else if( soundType == IMPACTSOUND_METAL ) {
  1455. sfx = cgs.media.sfx_nghitmetal;
  1456. } else {
  1457. sfx = cgs.media.sfx_nghit;
  1458. }
  1459. mark = cgs.media.holeMarkShader;
  1460. radius = 12;
  1461. break;
  1462. #endif
  1463. case WP_LIGHTNING:
  1464. // no explosion at LG impact, it is added with the beam
  1465. r = rand() & 3;
  1466. if ( r < 2 ) {
  1467. sfx = cgs.media.sfx_lghit2;
  1468. } else if ( r == 2 ) {
  1469. sfx = cgs.media.sfx_lghit1;
  1470. } else {
  1471. sfx = cgs.media.sfx_lghit3;
  1472. }
  1473. mark = cgs.media.holeMarkShader;
  1474. radius = 12;
  1475. break;
  1476. #ifdef MISSIONPACK
  1477. case WP_PROX_LAUNCHER:
  1478. mod = cgs.media.dishFlashModel;
  1479. shader = cgs.media.grenadeExplosionShader;
  1480. sfx = cgs.media.sfx_proxexp;
  1481. mark = cgs.media.burnMarkShader;
  1482. radius = 64;
  1483. light = 300;
  1484. isSprite = qtrue;
  1485. break;
  1486. #endif
  1487. case WP_GRENADE_LAUNCHER:
  1488. mod = cgs.media.dishFlashModel;
  1489. shader = cgs.media.grenadeExplosionShader;
  1490. sfx = cgs.media.sfx_rockexp;
  1491. mark = cgs.media.burnMarkShader;
  1492. radius = 64;
  1493. light = 300;
  1494. isSprite = qtrue;
  1495. break;
  1496. case WP_ROCKET_LAUNCHER:
  1497. mod = cgs.media.dishFlashModel;
  1498. shader = cgs.media.rocketExplosionShader;
  1499. sfx = cgs.media.sfx_rockexp;
  1500. mark = cgs.media.burnMarkShader;
  1501. radius = 64;
  1502. light = 300;
  1503. isSprite = qtrue;
  1504. duration = 1000;
  1505. lightColor[0] = 1;
  1506. lightColor[1] = 0.75;
  1507. lightColor[2] = 0.0;
  1508. if (cg_oldRocket.integer == 0) {
  1509. // explosion sprite animation
  1510. VectorMA( origin, 24, dir, sprOrg );
  1511. VectorScale( dir, 64, sprVel );
  1512. CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1400, 20, 30 );
  1513. }
  1514. break;
  1515. case WP_RAILGUN:
  1516. mod = cgs.media.ringFlashModel;
  1517. shader = cgs.media.railExplosionShader;
  1518. sfx = cgs.media.sfx_plasmaexp;
  1519. mark = cgs.media.energyMarkShader;
  1520. radius = 24;
  1521. break;
  1522. case WP_PLASMAGUN:
  1523. mod = cgs.media.ringFlashModel;
  1524. shader = cgs.media.plasmaExplosionShader;
  1525. sfx = cgs.media.sfx_plasmaexp;
  1526. mark = cgs.media.energyMarkShader;
  1527. radius = 16;
  1528. break;
  1529. case WP_BFG:
  1530. mod = cgs.media.dishFlashModel;
  1531. shader = cgs.media.bfgExplosionShader;
  1532. sfx = cgs.media.sfx_rockexp;
  1533. mark = cgs.media.burnMarkShader;
  1534. radius = 32;
  1535. isSprite = qtrue;
  1536. break;
  1537. case WP_SHOTGUN:
  1538. mod = cgs.media.bulletFlashModel;
  1539. shader = cgs.media.bulletExplosionShader;
  1540. mark = cgs.media.bulletMarkShader;
  1541. sfx = 0;
  1542. radius = 4;
  1543. break;
  1544. #ifdef MISSIONPACK
  1545. case WP_CHAINGUN:
  1546. mod = cgs.media.bulletFlashModel;
  1547. if( soundType == IMPACTSOUND_FLESH ) {
  1548. sfx = cgs.media.sfx_chghitflesh;
  1549. } else if( soundType == IMPACTSOUND_METAL ) {
  1550. sfx = cgs.media.sfx_chghitmetal;
  1551. } else {
  1552. sfx = cgs.media.sfx_chghit;
  1553. }
  1554. mark = cgs.media.bulletMarkShader;
  1555. r = rand() & 3;
  1556. if ( r < 2 ) {
  1557. sfx = cgs.media.sfx_ric1;
  1558. } else if ( r == 2 ) {
  1559. sfx = cgs.media.sfx_ric2;
  1560. } else {
  1561. sfx = cgs.media.sfx_ric3;
  1562. }
  1563. radius = 8;
  1564. break;
  1565. #endif
  1566. case WP_MACHINEGUN:
  1567. mod = cgs.media.bulletFlashModel;
  1568. shader = cgs.media.bulletExplosionShader;
  1569. mark = cgs.media.bulletMarkShader;
  1570. r = rand() & 3;
  1571. if ( r == 0 ) {
  1572. sfx = cgs.media.sfx_ric1;
  1573. } else if ( r == 1 ) {
  1574. sfx = cgs.media.sfx_ric2;
  1575. } else {
  1576. sfx = cgs.media.sfx_ric3;
  1577. }
  1578. radius = 8;
  1579. break;
  1580. }
  1581. if ( sfx ) {
  1582. trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, sfx );
  1583. }
  1584. //
  1585. // create the explosion
  1586. //
  1587. if ( mod ) {
  1588. le = CG_MakeExplosion( origin, dir,
  1589. mod, shader,
  1590. duration, isSprite );
  1591. le->light = light;
  1592. VectorCopy( lightColor, le->lightColor );
  1593. if ( weapon == WP_RAILGUN ) {
  1594. // colorize with client color
  1595. VectorCopy( cgs.clientinfo[clientNum].color1, le->color );
  1596. }
  1597. }
  1598. //
  1599. // impact mark
  1600. //
  1601. alphaFade = (mark == cgs.media.energyMarkShader); // plasma fades alpha, all others fade color
  1602. if ( weapon == WP_RAILGUN ) {
  1603. float *color;
  1604. // colorize with client color
  1605. color = cgs.clientinfo[clientNum].color2;
  1606. CG_ImpactMark( mark, origin, dir, random()*360, color[0],color[1], color[2],1, alphaFade, radius, qfalse );
  1607. } else {
  1608. CG_ImpactMark( mark, origin, dir, random()*360, 1,1,1,1, alphaFade, radius, qfalse );
  1609. }
  1610. }
  1611. /*
  1612. =================
  1613. CG_MissileHitPlayer
  1614. =================
  1615. */
  1616. void CG_MissileHitPlayer( int weapon, vec3_t origin, vec3_t dir, int entityNum ) {
  1617. CG_Bleed( origin, entityNum );
  1618. // some weapons will make an explosion with the blood, while
  1619. // others will just make the blood
  1620. switch ( weapon ) {
  1621. case WP_GRENADE_LAUNCHER:
  1622. case WP_ROCKET_LAUNCHER:
  1623. #ifdef MISSIONPACK
  1624. case WP_NAILGUN:
  1625. case WP_CHAINGUN:
  1626. case WP_PROX_LAUNCHER:
  1627. #endif
  1628. CG_MissileHitWall( weapon, 0, origin, dir, IMPACTSOUND_FLESH );
  1629. break;
  1630. default:
  1631. break;
  1632. }
  1633. }
  1634. /*
  1635. ============================================================================
  1636. SHOTGUN TRACING
  1637. ============================================================================
  1638. */
  1639. /*
  1640. ================
  1641. CG_ShotgunPellet
  1642. ================
  1643. */
  1644. static void CG_ShotgunPellet( vec3_t start, vec3_t end, int skipNum ) {
  1645. trace_t tr;
  1646. int sourceContentType, destContentType;
  1647. CG_Trace( &tr, start, NULL, NULL, end, skipNum, MASK_SHOT );
  1648. sourceContentType = trap_CM_PointContents( start, 0 );
  1649. destContentType = trap_CM_PointContents( tr.endpos, 0 );
  1650. // FIXME: should probably move this cruft into CG_BubbleTrail
  1651. if ( sourceContentType == destContentType ) {
  1652. if ( sourceContentType & CONTENTS_WATER ) {
  1653. CG_BubbleTrail( start, tr.endpos, 32 );
  1654. }
  1655. } else if ( sourceContentType & CONTENTS_WATER ) {
  1656. trace_t trace;
  1657. trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER );
  1658. CG_BubbleTrail( start, trace.endpos, 32 );
  1659. } else if ( destContentType & CONTENTS_WATER ) {
  1660. trace_t trace;
  1661. trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER );
  1662. CG_BubbleTrail( tr.endpos, trace.endpos, 32 );
  1663. }
  1664. if ( tr.surfaceFlags & SURF_NOIMPACT ) {
  1665. return;
  1666. }
  1667. if ( cg_entities[tr.entityNum].currentState.eType == ET_PLAYER ) {
  1668. CG_MissileHitPlayer( WP_SHOTGUN, tr.endpos, tr.plane.normal, tr.entityNum );
  1669. } else {
  1670. if ( tr.surfaceFlags & SURF_NOIMPACT ) {
  1671. // SURF_NOIMPACT will not make a flame puff or a mark
  1672. return;
  1673. }
  1674. if ( tr.surfaceFlags & SURF_METALSTEPS ) {
  1675. CG_MissileHitWall( WP_SHOTGUN, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_METAL );
  1676. } else {
  1677. CG_MissileHitWall( WP_SHOTGUN, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_DEFAULT );
  1678. }
  1679. }
  1680. }
  1681. /*
  1682. ================
  1683. CG_ShotgunPattern
  1684. Perform the same traces the server did to locate the
  1685. hit splashes
  1686. ================
  1687. */
  1688. static void CG_ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, int otherEntNum ) {
  1689. int i;
  1690. float r, u;
  1691. vec3_t end;
  1692. vec3_t forward, right, up;
  1693. // derive the right and up vectors from the forward vector, because
  1694. // the client won't have any other information
  1695. VectorNormalize2( origin2, forward );
  1696. PerpendicularVector( right, forward );
  1697. CrossProduct( forward, right, up );
  1698. // generate the "random" spread pattern
  1699. for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) {
  1700. r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
  1701. u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
  1702. VectorMA( origin, 8192 * 16, forward, end);
  1703. VectorMA (end, r, right, end);
  1704. VectorMA (end, u, up, end);
  1705. CG_ShotgunPellet( origin, end, otherEntNum );
  1706. }
  1707. }
  1708. /*
  1709. ==============
  1710. CG_ShotgunFire
  1711. ==============
  1712. */
  1713. void CG_ShotgunFire( entityState_t *es ) {
  1714. vec3_t v;
  1715. int contents;
  1716. VectorSubtract( es->origin2, es->pos.trBase, v );
  1717. VectorNormalize( v );
  1718. VectorScale( v, 32, v );
  1719. VectorAdd( es->pos.trBase, v, v );
  1720. if ( cgs.glconfig.hardwareType != GLHW_RAGEPRO ) {
  1721. // ragepro can't alpha fade, so don't even bother with smoke
  1722. vec3_t up;
  1723. contents = trap_CM_PointContents( es->pos.trBase, 0 );
  1724. if ( !( contents & CONTENTS_WATER ) ) {
  1725. VectorSet( up, 0, 0, 8 );
  1726. CG_SmokePuff( v, up, 32, 1, 1, 1, 0.33f, 900, cg.time, 0, LEF_PUFF_DONT_SCALE, cgs.media.shotgunSmokePuffShader );
  1727. }
  1728. }
  1729. CG_ShotgunPattern( es->pos.trBase, es->origin2, es->eventParm, es->otherEntityNum );
  1730. }
  1731. /*
  1732. ============================================================================
  1733. BULLETS
  1734. ============================================================================
  1735. */
  1736. /*
  1737. ===============
  1738. CG_Tracer
  1739. ===============
  1740. */
  1741. void CG_Tracer( vec3_t source, vec3_t dest ) {
  1742. vec3_t forward, right;
  1743. polyVert_t verts[4];
  1744. vec3_t line;
  1745. float len, begin, end;
  1746. vec3_t start, finish;
  1747. vec3_t midpoint;
  1748. // tracer
  1749. VectorSubtract( dest, source, forward );
  1750. len = VectorNormalize( forward );
  1751. // start at least a little ways from the muzzle
  1752. if ( len < 100 ) {
  1753. return;
  1754. }
  1755. begin = 50 + random() * (len - 60);
  1756. end = begin + cg_tracerLength.value;
  1757. if ( end > len ) {
  1758. end = len;
  1759. }
  1760. VectorMA( source, begin, forward, start );
  1761. VectorMA( source, end, forward, finish );
  1762. line[0] = DotProduct( forward, cg.refdef.viewaxis[1] );
  1763. line[1] = DotProduct( forward, cg.refdef.viewaxis[2] );
  1764. VectorScale( cg.refdef.viewaxis[1], line[1], right );
  1765. VectorMA( right, -line[0], cg.refdef.viewaxis[2], right );
  1766. VectorNormalize( right );
  1767. VectorMA( finish, cg_tracerWidth.value, right, verts[0].xyz );
  1768. verts[0].st[0] = 0;
  1769. verts[0].st[1] = 1;
  1770. verts[0].modulate[0] = 255;
  1771. verts[0].modulate[1] = 255;
  1772. verts[0].modulate[2] = 255;
  1773. verts[0].modulate[3] = 255;
  1774. VectorMA( finish, -cg_tracerWidth.value, right, verts[1].xyz );
  1775. verts[1].st[0] = 1;
  1776. verts[1].st[1] = 0;
  1777. verts[1].modulate[0] = 255;
  1778. verts[1].modulate[1] = 255;
  1779. verts[1].modulate[2] = 255;
  1780. verts[1].modulate[3] = 255;
  1781. VectorMA( start, -cg_tracerWidth.value, right, verts[2].xyz );
  1782. verts[2].st[0] = 1;
  1783. verts[2].st[1] = 1;
  1784. verts[2].modulate[0] = 255;
  1785. verts[2].modulate[1] = 255;
  1786. verts[2].modulate[2] = 255;
  1787. verts[2].modulate[3] = 255;
  1788. VectorMA( start, cg_tracerWidth.value, right, verts[3].xyz );
  1789. verts[3].st[0] = 0;
  1790. verts[3].st[1] = 0;
  1791. verts[3].modulate[0] = 255;
  1792. verts[3].modulate[1] = 255;
  1793. verts[3].modulate[2] = 255;
  1794. verts[3].modulate[3] = 255;
  1795. trap_R_AddPolyToScene( cgs.media.tracerShader, 4, verts );
  1796. midpoint[0] = ( start[0] + finish[0] ) * 0.5;
  1797. midpoint[1] = ( start[1] + finish[1] ) * 0.5;
  1798. midpoint[2] = ( start[2] + finish[2] ) * 0.5;
  1799. // add the tracer sound
  1800. trap_S_StartSound( midpoint, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.tracerSound );
  1801. }
  1802. /*
  1803. ======================
  1804. CG_CalcMuzzlePoint
  1805. ======================
  1806. */
  1807. static qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle ) {
  1808. vec3_t forward;
  1809. centity_t *cent;
  1810. int anim;
  1811. if ( entityNum == cg.snap->ps.clientNum ) {
  1812. VectorCopy( cg.snap->ps.origin, muzzle );
  1813. muzzle[2] += cg.snap->ps.viewheight;
  1814. AngleVectors( cg.snap->ps.viewangles, forward, NULL, NULL );
  1815. VectorMA( muzzle, 14, forward, muzzle );
  1816. return qtrue;
  1817. }
  1818. cent = &cg_entities[entityNum];
  1819. if ( !cent->currentValid ) {
  1820. return qfalse;
  1821. }
  1822. VectorCopy( cent->currentState.pos.trBase, muzzle );
  1823. AngleVectors( cent->currentState.apos.trBase, forward, NULL, NULL );
  1824. anim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT;
  1825. if ( anim == LEGS_WALKCR || anim == LEGS_IDLECR ) {
  1826. muzzle[2] += CROUCH_VIEWHEIGHT;
  1827. } else {
  1828. muzzle[2] += DEFAULT_VIEWHEIGHT;
  1829. }
  1830. VectorMA( muzzle, 14, forward, muzzle );
  1831. return qtrue;
  1832. }
  1833. /*
  1834. ======================
  1835. CG_Bullet
  1836. Renders bullet effects.
  1837. ======================
  1838. */
  1839. void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum ) {
  1840. trace_t trace;
  1841. int sourceContentType, destContentType;
  1842. vec3_t start;
  1843. // if the shooter is currently valid, calc a source point and possibly
  1844. // do trail effects
  1845. if ( sourceEntityNum >= 0 && cg_tracerChance.value > 0 ) {
  1846. if ( CG_CalcMuzzlePoint( sourceEntityNum, start ) ) {
  1847. sourceContentType = trap_CM_PointContents( start, 0 );
  1848. destContentType = trap_CM_PointContents( end, 0 );
  1849. // do a complete bubble trail if necessary
  1850. if ( ( sourceContentType == destContentType ) && ( sourceContentType & CONTENTS_WATER ) ) {
  1851. CG_BubbleTrail( start, end, 32 );
  1852. }
  1853. // bubble trail from water into air
  1854. else if ( ( sourceContentType & CONTENTS_WATER ) ) {
  1855. trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER );
  1856. CG_BubbleTrail( start, trace.endpos, 32 );
  1857. }
  1858. // bubble trail from air into water
  1859. else if ( ( destContentType & CONTENTS_WATER ) ) {
  1860. trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER );
  1861. CG_BubbleTrail( trace.endpos, end, 32 );
  1862. }
  1863. // draw a tracer
  1864. if ( random() < cg_tracerChance.value ) {
  1865. CG_Tracer( start, end );
  1866. }
  1867. }
  1868. }
  1869. // impact splash and mark
  1870. if ( flesh ) {
  1871. CG_Bleed( end, fleshEntityNum );
  1872. } else {
  1873. CG_MissileHitWall( WP_MACHINEGUN, 0, end, normal, IMPACTSOUND_DEFAULT );
  1874. }
  1875. }