Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

cg_weapons.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 // cg_weapons.c -- events and effects dealing with weapons
00024 #include "cg_local.h"
00025 
00026 /*
00027 ==========================
00028 CG_MachineGunEjectBrass
00029 ==========================
00030 */
00031 static void CG_MachineGunEjectBrass( centity_t *cent ) {
00032     localEntity_t   *le;
00033     refEntity_t     *re;
00034     vec3_t          velocity, xvelocity;
00035     vec3_t          offset, xoffset;
00036     float           waterScale = 1.0f;
00037     vec3_t          v[3];
00038 
00039     if ( cg_brassTime.integer <= 0 ) {
00040         return;
00041     }
00042 
00043     le = CG_AllocLocalEntity();
00044     re = &le->refEntity;
00045 
00046     velocity[0] = 0;
00047     velocity[1] = -50 + 40 * crandom();
00048     velocity[2] = 100 + 50 * crandom();
00049 
00050     le->leType = LE_FRAGMENT;
00051     le->startTime = cg.time;
00052     le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random();
00053 
00054     le->pos.trType = TR_GRAVITY;
00055     le->pos.trTime = cg.time - (rand()&15);
00056 
00057     AnglesToAxis( cent->lerpAngles, v );
00058 
00059     offset[0] = 8;
00060     offset[1] = -4;
00061     offset[2] = 24;
00062 
00063     xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
00064     xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
00065     xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
00066     VectorAdd( cent->lerpOrigin, xoffset, re->origin );
00067 
00068     VectorCopy( re->origin, le->pos.trBase );
00069 
00070     if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
00071         waterScale = 0.10f;
00072     }
00073 
00074     xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
00075     xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
00076     xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
00077     VectorScale( xvelocity, waterScale, le->pos.trDelta );
00078 
00079     AxisCopy( axisDefault, re->axis );
00080     re->hModel = cgs.media.machinegunBrassModel;
00081 
00082     le->bounceFactor = 0.4 * waterScale;
00083 
00084     le->angles.trType = TR_LINEAR;
00085     le->angles.trTime = cg.time;
00086     le->angles.trBase[0] = rand()&31;
00087     le->angles.trBase[1] = rand()&31;
00088     le->angles.trBase[2] = rand()&31;
00089     le->angles.trDelta[0] = 2;
00090     le->angles.trDelta[1] = 1;
00091     le->angles.trDelta[2] = 0;
00092 
00093     le->leFlags = LEF_TUMBLE;
00094     le->leBounceSoundType = LEBS_BRASS;
00095     le->leMarkType = LEMT_NONE;
00096 }
00097 
00098 /*
00099 ==========================
00100 CG_ShotgunEjectBrass
00101 ==========================
00102 */
00103 static void CG_ShotgunEjectBrass( centity_t *cent ) {
00104     localEntity_t   *le;
00105     refEntity_t     *re;
00106     vec3_t          velocity, xvelocity;
00107     vec3_t          offset, xoffset;
00108     vec3_t          v[3];
00109     int             i;
00110 
00111     if ( cg_brassTime.integer <= 0 ) {
00112         return;
00113     }
00114 
00115     for ( i = 0; i < 2; i++ ) {
00116         float   waterScale = 1.0f;
00117 
00118         le = CG_AllocLocalEntity();
00119         re = &le->refEntity;
00120 
00121         velocity[0] = 60 + 60 * crandom();
00122         if ( i == 0 ) {
00123             velocity[1] = 40 + 10 * crandom();
00124         } else {
00125             velocity[1] = -40 + 10 * crandom();
00126         }
00127         velocity[2] = 100 + 50 * crandom();
00128 
00129         le->leType = LE_FRAGMENT;
00130         le->startTime = cg.time;
00131         le->endTime = le->startTime + cg_brassTime.integer*3 + cg_brassTime.integer * random();
00132 
00133         le->pos.trType = TR_GRAVITY;
00134         le->pos.trTime = cg.time;
00135 
00136         AnglesToAxis( cent->lerpAngles, v );
00137 
00138         offset[0] = 8;
00139         offset[1] = 0;
00140         offset[2] = 24;
00141 
00142         xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
00143         xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
00144         xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
00145         VectorAdd( cent->lerpOrigin, xoffset, re->origin );
00146         VectorCopy( re->origin, le->pos.trBase );
00147         if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
00148             waterScale = 0.10f;
00149         }
00150 
00151         xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
00152         xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
00153         xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
00154         VectorScale( xvelocity, waterScale, le->pos.trDelta );
00155 
00156         AxisCopy( axisDefault, re->axis );
00157         re->hModel = cgs.media.shotgunBrassModel;
00158         le->bounceFactor = 0.3f;
00159 
00160         le->angles.trType = TR_LINEAR;
00161         le->angles.trTime = cg.time;
00162         le->angles.trBase[0] = rand()&31;
00163         le->angles.trBase[1] = rand()&31;
00164         le->angles.trBase[2] = rand()&31;
00165         le->angles.trDelta[0] = 1;
00166         le->angles.trDelta[1] = 0.5;
00167         le->angles.trDelta[2] = 0;
00168 
00169         le->leFlags = LEF_TUMBLE;
00170         le->leBounceSoundType = LEBS_BRASS;
00171         le->leMarkType = LEMT_NONE;
00172     }
00173 }
00174 
00175 
00176 #ifdef MISSIONPACK
00177 /*
00178 ==========================
00179 CG_NailgunEjectBrass
00180 ==========================
00181 */
00182 static void CG_NailgunEjectBrass( centity_t *cent ) {
00183     localEntity_t   *smoke;
00184     vec3_t          origin;
00185     vec3_t          v[3];
00186     vec3_t          offset;
00187     vec3_t          xoffset;
00188     vec3_t          up;
00189 
00190     AnglesToAxis( cent->lerpAngles, v );
00191 
00192     offset[0] = 0;
00193     offset[1] = -12;
00194     offset[2] = 24;
00195 
00196     xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
00197     xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
00198     xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
00199     VectorAdd( cent->lerpOrigin, xoffset, origin );
00200 
00201     VectorSet( up, 0, 0, 64 );
00202 
00203     smoke = CG_SmokePuff( origin, up, 32, 1, 1, 1, 0.33f, 700, cg.time, 0, 0, cgs.media.smokePuffShader );
00204     // use the optimized local entity add
00205     smoke->leType = LE_SCALE_FADE;
00206 }
00207 #endif
00208 
00209 
00210 /*
00211 ==========================
00212 CG_RailTrail
00213 ==========================
00214 */
00215 void CG_RailTrail (clientInfo_t *ci, vec3_t start, vec3_t end) {
00216     vec3_t axis[36], move, move2, next_move, vec, temp;
00217     float  len;
00218     int    i, j, skip;
00219  
00220     localEntity_t *le;
00221     refEntity_t   *re;
00222  
00223 #define RADIUS   4
00224 #define ROTATION 1
00225 #define SPACING  5
00226  
00227     start[2] -= 4;
00228     VectorCopy (start, move);
00229     VectorSubtract (end, start, vec);
00230     len = VectorNormalize (vec);
00231     PerpendicularVector(temp, vec);
00232     for (i = 0 ; i < 36; i++) {
00233         RotatePointAroundVector(axis[i], vec, temp, i * 10);//banshee 2.4 was 10
00234     }
00235  
00236     le = CG_AllocLocalEntity();
00237     re = &le->refEntity;
00238  
00239     le->leType = LE_FADE_RGB;
00240     le->startTime = cg.time;
00241     le->endTime = cg.time + cg_railTrailTime.value;
00242     le->lifeRate = 1.0 / (le->endTime - le->startTime);
00243  
00244     re->shaderTime = cg.time / 1000.0f;
00245     re->reType = RT_RAIL_CORE;
00246     re->customShader = cgs.media.railCoreShader;
00247  
00248     VectorCopy(start, re->origin);
00249     VectorCopy(end, re->oldorigin);
00250  
00251     re->shaderRGBA[0] = ci->color1[0] * 255;
00252     re->shaderRGBA[1] = ci->color1[1] * 255;
00253     re->shaderRGBA[2] = ci->color1[2] * 255;
00254     re->shaderRGBA[3] = 255;
00255 
00256     le->color[0] = ci->color1[0] * 0.75;
00257     le->color[1] = ci->color1[1] * 0.75;
00258     le->color[2] = ci->color1[2] * 0.75;
00259     le->color[3] = 1.0f;
00260 
00261     AxisClear( re->axis );
00262  
00263     VectorMA(move, 20, vec, move);
00264     VectorCopy(move, next_move);
00265     VectorScale (vec, SPACING, vec);
00266 
00267     if (cg_oldRail.integer != 0) {
00268         // nudge down a bit so it isn't exactly in center
00269         re->origin[2] -= 8;
00270         re->oldorigin[2] -= 8;
00271         return;
00272     }
00273     skip = -1;
00274  
00275     j = 18;
00276     for (i = 0; i < len; i += SPACING) {
00277         if (i != skip) {
00278             skip = i + SPACING;
00279             le = CG_AllocLocalEntity();
00280             re = &le->refEntity;
00281             le->leFlags = LEF_PUFF_DONT_SCALE;
00282             le->leType = LE_MOVE_SCALE_FADE;
00283             le->startTime = cg.time;
00284             le->endTime = cg.time + (i>>1) + 600;
00285             le->lifeRate = 1.0 / (le->endTime - le->startTime);
00286 
00287             re->shaderTime = cg.time / 1000.0f;
00288             re->reType = RT_SPRITE;
00289             re->radius = 1.1f;
00290             re->customShader = cgs.media.railRingsShader;
00291 
00292             re->shaderRGBA[0] = ci->color2[0] * 255;
00293             re->shaderRGBA[1] = ci->color2[1] * 255;
00294             re->shaderRGBA[2] = ci->color2[2] * 255;
00295             re->shaderRGBA[3] = 255;
00296 
00297             le->color[0] = ci->color2[0] * 0.75;
00298             le->color[1] = ci->color2[1] * 0.75;
00299             le->color[2] = ci->color2[2] * 0.75;
00300             le->color[3] = 1.0f;
00301 
00302             le->pos.trType = TR_LINEAR;
00303             le->pos.trTime = cg.time;
00304 
00305             VectorCopy( move, move2);
00306             VectorMA(move2, RADIUS , axis[j], move2);
00307             VectorCopy(move2, le->pos.trBase);
00308 
00309             le->pos.trDelta[0] = axis[j][0]*6;
00310             le->pos.trDelta[1] = axis[j][1]*6;
00311             le->pos.trDelta[2] = axis[j][2]*6;
00312         }
00313 
00314         VectorAdd (move, vec, move);
00315 
00316         j = j + ROTATION < 36 ? j + ROTATION : (j + ROTATION) % 36;
00317     }
00318 }
00319 
00320 /*
00321 ==========================
00322 CG_RocketTrail
00323 ==========================
00324 */
00325 static void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) {
00326     int     step;
00327     vec3_t  origin, lastPos;
00328     int     t;
00329     int     startTime, contents;
00330     int     lastContents;
00331     entityState_t   *es;
00332     vec3_t  up;
00333     localEntity_t   *smoke;
00334 
00335     if ( cg_noProjectileTrail.integer ) {
00336         return;
00337     }
00338 
00339     up[0] = 0;
00340     up[1] = 0;
00341     up[2] = 0;
00342 
00343     step = 50;
00344 
00345     es = &ent->currentState;
00346     startTime = ent->trailTime;
00347     t = step * ( (startTime + step) / step );
00348 
00349     BG_EvaluateTrajectory( &es->pos, cg.time, origin );
00350     contents = CG_PointContents( origin, -1 );
00351 
00352     // if object (e.g. grenade) is stationary, don't toss up smoke
00353     if ( es->pos.trType == TR_STATIONARY ) {
00354         ent->trailTime = cg.time;
00355         return;
00356     }
00357 
00358     BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
00359     lastContents = CG_PointContents( lastPos, -1 );
00360 
00361     ent->trailTime = cg.time;
00362 
00363     if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
00364         if ( contents & lastContents & CONTENTS_WATER ) {
00365             CG_BubbleTrail( lastPos, origin, 8 );
00366         }
00367         return;
00368     }
00369 
00370     for ( ; t <= ent->trailTime ; t += step ) {
00371         BG_EvaluateTrajectory( &es->pos, t, lastPos );
00372 
00373         smoke = CG_SmokePuff( lastPos, up, 
00374                       wi->trailRadius, 
00375                       1, 1, 1, 0.33f,
00376                       wi->wiTrailTime, 
00377                       t,
00378                       0,
00379                       0, 
00380                       cgs.media.smokePuffShader );
00381         // use the optimized local entity add
00382         smoke->leType = LE_SCALE_FADE;
00383     }
00384 
00385 }
00386 
00387 #ifdef MISSIONPACK
00388 /*
00389 ==========================
00390 CG_NailTrail
00391 ==========================
00392 */
00393 static void CG_NailTrail( centity_t *ent, const weaponInfo_t *wi ) {
00394     int     step;
00395     vec3_t  origin, lastPos;
00396     int     t;
00397     int     startTime, contents;
00398     int     lastContents;
00399     entityState_t   *es;
00400     vec3_t  up;
00401     localEntity_t   *smoke;
00402 
00403     if ( cg_noProjectileTrail.integer ) {
00404         return;
00405     }
00406 
00407     up[0] = 0;
00408     up[1] = 0;
00409     up[2] = 0;
00410 
00411     step = 50;
00412 
00413     es = &ent->currentState;
00414     startTime = ent->trailTime;
00415     t = step * ( (startTime + step) / step );
00416 
00417     BG_EvaluateTrajectory( &es->pos, cg.time, origin );
00418     contents = CG_PointContents( origin, -1 );
00419 
00420     // if object (e.g. grenade) is stationary, don't toss up smoke
00421     if ( es->pos.trType == TR_STATIONARY ) {
00422         ent->trailTime = cg.time;
00423         return;
00424     }
00425 
00426     BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
00427     lastContents = CG_PointContents( lastPos, -1 );
00428 
00429     ent->trailTime = cg.time;
00430 
00431     if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
00432         if ( contents & lastContents & CONTENTS_WATER ) {
00433             CG_BubbleTrail( lastPos, origin, 8 );
00434         }
00435         return;
00436     }
00437 
00438     for ( ; t <= ent->trailTime ; t += step ) {
00439         BG_EvaluateTrajectory( &es->pos, t, lastPos );
00440 
00441         smoke = CG_SmokePuff( lastPos, up, 
00442                       wi->trailRadius, 
00443                       1, 1, 1, 0.33f,
00444                       wi->wiTrailTime, 
00445                       t,
00446                       0,
00447                       0, 
00448                       cgs.media.nailPuffShader );
00449         // use the optimized local entity add
00450         smoke->leType = LE_SCALE_FADE;
00451     }
00452 
00453 }
00454 #endif
00455 
00456 /*
00457 ==========================
00458 CG_NailTrail
00459 ==========================
00460 */
00461 static void CG_PlasmaTrail( centity_t *cent, const weaponInfo_t *wi ) {
00462     localEntity_t   *le;
00463     refEntity_t     *re;
00464     entityState_t   *es;
00465     vec3_t          velocity, xvelocity, origin;
00466     vec3_t          offset, xoffset;
00467     vec3_t          v[3];
00468     int             t, startTime, step;
00469 
00470     float   waterScale = 1.0f;
00471 
00472     if ( cg_noProjectileTrail.integer || cg_oldPlasma.integer ) {
00473         return;
00474     }
00475 
00476     step = 50;
00477 
00478     es = &cent->currentState;
00479     startTime = cent->trailTime;
00480     t = step * ( (startTime + step) / step );
00481 
00482     BG_EvaluateTrajectory( &es->pos, cg.time, origin );
00483 
00484     le = CG_AllocLocalEntity();
00485     re = &le->refEntity;
00486 
00487     velocity[0] = 60 - 120 * crandom();
00488     velocity[1] = 40 - 80 * crandom();
00489     velocity[2] = 100 - 200 * crandom();
00490 
00491     le->leType = LE_MOVE_SCALE_FADE;
00492     le->leFlags = LEF_TUMBLE;
00493     le->leBounceSoundType = LEBS_NONE;
00494     le->leMarkType = LEMT_NONE;
00495 
00496     le->startTime = cg.time;
00497     le->endTime = le->startTime + 600;
00498 
00499     le->pos.trType = TR_GRAVITY;
00500     le->pos.trTime = cg.time;
00501 
00502     AnglesToAxis( cent->lerpAngles, v );
00503 
00504     offset[0] = 2;
00505     offset[1] = 2;
00506     offset[2] = 2;
00507 
00508     xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
00509     xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
00510     xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
00511 
00512     VectorAdd( origin, xoffset, re->origin );
00513     VectorCopy( re->origin, le->pos.trBase );
00514 
00515     if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
00516         waterScale = 0.10f;
00517     }
00518 
00519     xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
00520     xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
00521     xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
00522     VectorScale( xvelocity, waterScale, le->pos.trDelta );
00523 
00524     AxisCopy( axisDefault, re->axis );
00525     re->shaderTime = cg.time / 1000.0f;
00526     re->reType = RT_SPRITE;
00527     re->radius = 0.25f;
00528     re->customShader = cgs.media.railRingsShader;
00529     le->bounceFactor = 0.3f;
00530 
00531     re->shaderRGBA[0] = wi->flashDlightColor[0] * 63;
00532     re->shaderRGBA[1] = wi->flashDlightColor[1] * 63;
00533     re->shaderRGBA[2] = wi->flashDlightColor[2] * 63;
00534     re->shaderRGBA[3] = 63;
00535 
00536     le->color[0] = wi->flashDlightColor[0] * 0.2;
00537     le->color[1] = wi->flashDlightColor[1] * 0.2;
00538     le->color[2] = wi->flashDlightColor[2] * 0.2;
00539     le->color[3] = 0.25f;
00540 
00541     le->angles.trType = TR_LINEAR;
00542     le->angles.trTime = cg.time;
00543     le->angles.trBase[0] = rand()&31;
00544     le->angles.trBase[1] = rand()&31;
00545     le->angles.trBase[2] = rand()&31;
00546     le->angles.trDelta[0] = 1;
00547     le->angles.trDelta[1] = 0.5;
00548     le->angles.trDelta[2] = 0;
00549 
00550 }
00551 /*
00552 ==========================
00553 CG_GrappleTrail
00554 ==========================
00555 */
00556 void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ) {
00557     vec3_t  origin;
00558     entityState_t   *es;
00559     vec3_t          forward, up;
00560     refEntity_t     beam;
00561 
00562     es = &ent->currentState;
00563 
00564     BG_EvaluateTrajectory( &es->pos, cg.time, origin );
00565     ent->trailTime = cg.time;
00566 
00567     memset( &beam, 0, sizeof( beam ) );
00568     //FIXME adjust for muzzle position
00569     VectorCopy ( cg_entities[ ent->currentState.otherEntityNum ].lerpOrigin, beam.origin );
00570     beam.origin[2] += 26;
00571     AngleVectors( cg_entities[ ent->currentState.otherEntityNum ].lerpAngles, forward, NULL, up );
00572     VectorMA( beam.origin, -6, up, beam.origin );
00573     VectorCopy( origin, beam.oldorigin );
00574 
00575     if (Distance( beam.origin, beam.oldorigin ) < 64 )
00576         return; // Don't draw if close
00577 
00578     beam.reType = RT_LIGHTNING;
00579     beam.customShader = cgs.media.lightningShader;
00580 
00581     AxisClear( beam.axis );
00582     beam.shaderRGBA[0] = 0xff;
00583     beam.shaderRGBA[1] = 0xff;
00584     beam.shaderRGBA[2] = 0xff;
00585     beam.shaderRGBA[3] = 0xff;
00586     trap_R_AddRefEntityToScene( &beam );
00587 }
00588 
00589 /*
00590 ==========================
00591 CG_GrenadeTrail
00592 ==========================
00593 */
00594 static void CG_GrenadeTrail( centity_t *ent, const weaponInfo_t *wi ) {
00595     CG_RocketTrail( ent, wi );
00596 }
00597 
00598 
00599 /*
00600 =================
00601 CG_RegisterWeapon
00602 
00603 The server says this item is used on this level
00604 =================
00605 */
00606 void CG_RegisterWeapon( int weaponNum ) {
00607     weaponInfo_t    *weaponInfo;
00608     gitem_t         *item, *ammo;
00609     char            path[MAX_QPATH];
00610     vec3_t          mins, maxs;
00611     int             i;
00612 
00613     weaponInfo = &cg_weapons[weaponNum];
00614 
00615     if ( weaponNum == 0 ) {
00616         return;
00617     }
00618 
00619     if ( weaponInfo->registered ) {
00620         return;
00621     }
00622 
00623     memset( weaponInfo, 0, sizeof( *weaponInfo ) );
00624     weaponInfo->registered = qtrue;
00625 
00626     for ( item = bg_itemlist + 1 ; item->classname ; item++ ) {
00627         if ( item->giType == IT_WEAPON && item->giTag == weaponNum ) {
00628             weaponInfo->item = item;
00629             break;
00630         }
00631     }
00632     if ( !item->classname ) {
00633         CG_Error( "Couldn't find weapon %i", weaponNum );
00634     }
00635     CG_RegisterItemVisuals( item - bg_itemlist );
00636 
00637     // load cmodel before model so filecache works
00638     weaponInfo->weaponModel = trap_R_RegisterModel( item->world_model[0] );
00639 
00640     // calc midpoint for rotation
00641     trap_R_ModelBounds( weaponInfo->weaponModel, mins, maxs );
00642     for ( i = 0 ; i < 3 ; i++ ) {
00643         weaponInfo->weaponMidpoint[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] );
00644     }
00645 
00646     weaponInfo->weaponIcon = trap_R_RegisterShader( item->icon );
00647     weaponInfo->ammoIcon = trap_R_RegisterShader( item->icon );
00648 
00649     for ( ammo = bg_itemlist + 1 ; ammo->classname ; ammo++ ) {
00650         if ( ammo->giType == IT_AMMO && ammo->giTag == weaponNum ) {
00651             break;
00652         }
00653     }
00654     if ( ammo->classname && ammo->world_model[0] ) {
00655         weaponInfo->ammoModel = trap_R_RegisterModel( ammo->world_model[0] );
00656     }
00657 
00658     strcpy( path, item->world_model[0] );
00659     COM_StripExtension( path, path );
00660     strcat( path, "_flash.md3" );
00661     weaponInfo->flashModel = trap_R_RegisterModel( path );
00662 
00663     strcpy( path, item->world_model[0] );
00664     COM_StripExtension( path, path );
00665     strcat( path, "_barrel.md3" );
00666     weaponInfo->barrelModel = trap_R_RegisterModel( path );
00667 
00668     strcpy( path, item->world_model[0] );
00669     COM_StripExtension( path, path );
00670     strcat( path, "_hand.md3" );
00671     weaponInfo->handsModel = trap_R_RegisterModel( path );
00672 
00673     if ( !weaponInfo->handsModel ) {
00674         weaponInfo->handsModel = trap_R_RegisterModel( "models/weapons2/shotgun/shotgun_hand.md3" );
00675     }
00676 
00677     weaponInfo->loopFireSound = qfalse;
00678 
00679     switch ( weaponNum ) {
00680     case WP_GAUNTLET:
00681         MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
00682         weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse );
00683         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/melee/fstatck.wav", qfalse );
00684         break;
00685 
00686     case WP_LIGHTNING:
00687         MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
00688         weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse );
00689         weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/lightning/lg_hum.wav", qfalse );
00690 
00691         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/lightning/lg_fire.wav", qfalse );
00692         cgs.media.lightningShader = trap_R_RegisterShader( "lightningBoltNew");
00693         cgs.media.lightningExplosionModel = trap_R_RegisterModel( "models/weaphits/crackle.md3" );
00694         cgs.media.sfx_lghit1 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit.wav", qfalse );
00695         cgs.media.sfx_lghit2 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit2.wav", qfalse );
00696         cgs.media.sfx_lghit3 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit3.wav", qfalse );
00697 
00698         break;
00699 
00700     case WP_GRAPPLING_HOOK:
00701         MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
00702         weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" );
00703         weaponInfo->missileTrailFunc = CG_GrappleTrail;
00704         weaponInfo->missileDlight = 200;
00705         weaponInfo->wiTrailTime = 2000;
00706         weaponInfo->trailRadius = 64;
00707         MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 );
00708         weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse );
00709         weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse );
00710         break;
00711 
00712 #ifdef MISSIONPACK
00713     case WP_CHAINGUN:
00714         weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/vulcan/wvulfire.wav", qfalse );
00715         weaponInfo->loopFireSound = qtrue;
00716         MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 );
00717         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf1b.wav", qfalse );
00718         weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf2b.wav", qfalse );
00719         weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf3b.wav", qfalse );
00720         weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf4b.wav", qfalse );
00721         weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
00722         cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" );
00723         break;
00724 #endif
00725 
00726     case WP_MACHINEGUN:
00727         MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 );
00728         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf1b.wav", qfalse );
00729         weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf2b.wav", qfalse );
00730         weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf3b.wav", qfalse );
00731         weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf4b.wav", qfalse );
00732         weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
00733         cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" );
00734         break;
00735 
00736     case WP_SHOTGUN:
00737         MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 );
00738         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/shotgun/sshotf1b.wav", qfalse );
00739         weaponInfo->ejectBrassFunc = CG_ShotgunEjectBrass;
00740         break;
00741 
00742     case WP_ROCKET_LAUNCHER:
00743         weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" );
00744         weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse );
00745         weaponInfo->missileTrailFunc = CG_RocketTrail;
00746         weaponInfo->missileDlight = 200;
00747         weaponInfo->wiTrailTime = 2000;
00748         weaponInfo->trailRadius = 64;
00749         
00750         MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 );
00751         MAKERGB( weaponInfo->flashDlightColor, 1, 0.75f, 0 );
00752 
00753         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav", qfalse );
00754         cgs.media.rocketExplosionShader = trap_R_RegisterShader( "rocketExplosion" );
00755         break;
00756 
00757 #ifdef MISSIONPACK
00758     case WP_PROX_LAUNCHER:
00759         weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/proxmine.md3" );
00760         weaponInfo->missileTrailFunc = CG_GrenadeTrail;
00761         weaponInfo->wiTrailTime = 700;
00762         weaponInfo->trailRadius = 32;
00763         MAKERGB( weaponInfo->flashDlightColor, 1, 0.70f, 0 );
00764         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/proxmine/wstbfire.wav", qfalse );
00765         cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" );
00766         break;
00767 #endif
00768 
00769     case WP_GRENADE_LAUNCHER:
00770         weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/grenade1.md3" );
00771         weaponInfo->missileTrailFunc = CG_GrenadeTrail;
00772         weaponInfo->wiTrailTime = 700;
00773         weaponInfo->trailRadius = 32;
00774         MAKERGB( weaponInfo->flashDlightColor, 1, 0.70f, 0 );
00775         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/grenade/grenlf1a.wav", qfalse );
00776         cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" );
00777         break;
00778 
00779 #ifdef MISSIONPACK
00780     case WP_NAILGUN:
00781         weaponInfo->ejectBrassFunc = CG_NailgunEjectBrass;
00782         weaponInfo->missileTrailFunc = CG_NailTrail;
00783 //      weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/nailgun/wnalflit.wav", qfalse );
00784         weaponInfo->trailRadius = 16;
00785         weaponInfo->wiTrailTime = 250;
00786         weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/nail.md3" );
00787         MAKERGB( weaponInfo->flashDlightColor, 1, 0.75f, 0 );
00788         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/nailgun/wnalfire.wav", qfalse );
00789         break;
00790 #endif
00791 
00792     case WP_PLASMAGUN:
00793 //      weaponInfo->missileModel = cgs.media.invulnerabilityPowerupModel;
00794         weaponInfo->missileTrailFunc = CG_PlasmaTrail;
00795         weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/plasma/lasfly.wav", qfalse );
00796         MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
00797         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/plasma/hyprbf1a.wav", qfalse );
00798         cgs.media.plasmaExplosionShader = trap_R_RegisterShader( "plasmaExplosion" );
00799         cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" );
00800         break;
00801 
00802     case WP_RAILGUN:
00803         weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/railgun/rg_hum.wav", qfalse );
00804         MAKERGB( weaponInfo->flashDlightColor, 1, 0.5f, 0 );
00805         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/railgun/railgf1a.wav", qfalse );
00806         cgs.media.railExplosionShader = trap_R_RegisterShader( "railExplosion" );
00807         cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" );
00808         cgs.media.railCoreShader = trap_R_RegisterShader( "railCore" );
00809         break;
00810 
00811     case WP_BFG:
00812         weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/bfg/bfg_hum.wav", qfalse );
00813         MAKERGB( weaponInfo->flashDlightColor, 1, 0.7f, 1 );
00814         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/bfg/bfg_fire.wav", qfalse );
00815         cgs.media.bfgExplosionShader = trap_R_RegisterShader( "bfgExplosion" );
00816         weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/bfg.md3" );
00817         weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse );
00818         break;
00819 
00820      default:
00821         MAKERGB( weaponInfo->flashDlightColor, 1, 1, 1 );
00822         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav", qfalse );
00823         break;
00824     }
00825 }
00826 
00827 /*
00828 =================
00829 CG_RegisterItemVisuals
00830 
00831 The server says this item is used on this level
00832 =================
00833 */
00834 void CG_RegisterItemVisuals( int itemNum ) {
00835     itemInfo_t      *itemInfo;
00836     gitem_t         *item;
00837 
00838     if ( itemNum < 0 || itemNum >= bg_numItems ) {
00839         CG_Error( "CG_RegisterItemVisuals: itemNum %d out of range [0-%d]", itemNum, bg_numItems-1 );
00840     }
00841 
00842     itemInfo = &cg_items[ itemNum ];
00843     if ( itemInfo->registered ) {
00844         return;
00845     }
00846 
00847     item = &bg_itemlist[ itemNum ];
00848 
00849     memset( itemInfo, 0, sizeof( &itemInfo ) );
00850     itemInfo->registered = qtrue;
00851 
00852     itemInfo->models[0] = trap_R_RegisterModel( item->world_model[0] );
00853 
00854     itemInfo->icon = trap_R_RegisterShader( item->icon );
00855 
00856     if ( item->giType == IT_WEAPON ) {
00857         CG_RegisterWeapon( item->giTag );
00858     }
00859 
00860     //
00861     // powerups have an accompanying ring or sphere
00862     //
00863     if ( item->giType == IT_POWERUP || item->giType == IT_HEALTH || 
00864         item->giType == IT_ARMOR || item->giType == IT_HOLDABLE ) {
00865         if ( item->world_model[1] ) {
00866             itemInfo->models[1] = trap_R_RegisterModel( item->world_model[1] );
00867         }
00868     }
00869 }
00870 
00871 
00872 /*
00873 ========================================================================================
00874 
00875 VIEW WEAPON
00876 
00877 ========================================================================================
00878 */
00879 
00880 /*
00881 =================
00882 CG_MapTorsoToWeaponFrame
00883 
00884 =================
00885 */
00886 static int CG_MapTorsoToWeaponFrame( clientInfo_t *ci, int frame ) {
00887 
00888     // change weapon
00889     if ( frame >= ci->animations[TORSO_DROP].firstFrame 
00890         && frame < ci->animations[TORSO_DROP].firstFrame + 9 ) {
00891         return frame - ci->animations[TORSO_DROP].firstFrame + 6;
00892     }
00893 
00894     // stand attack
00895     if ( frame >= ci->animations[TORSO_ATTACK].firstFrame 
00896         && frame < ci->animations[TORSO_ATTACK].firstFrame + 6 ) {
00897         return 1 + frame - ci->animations[TORSO_ATTACK].firstFrame;
00898     }
00899 
00900     // stand attack 2
00901     if ( frame >= ci->animations[TORSO_ATTACK2].firstFrame 
00902         && frame < ci->animations[TORSO_ATTACK2].firstFrame + 6 ) {
00903         return 1 + frame - ci->animations[TORSO_ATTACK2].firstFrame;
00904     }
00905     
00906     return 0;
00907 }
00908 
00909 
00910 /*
00911 ==============
00912 CG_CalculateWeaponPosition
00913 ==============
00914 */
00915 static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) {
00916     float   scale;
00917     int     delta;
00918     float   fracsin;
00919 
00920     VectorCopy( cg.refdef.vieworg, origin );
00921     VectorCopy( cg.refdefViewAngles, angles );
00922 
00923     // on odd legs, invert some angles
00924     if ( cg.bobcycle & 1 ) {
00925         scale = -cg.xyspeed;
00926     } else {
00927         scale = cg.xyspeed;
00928     }
00929 
00930     // gun angles from bobbing
00931     angles[ROLL] += scale * cg.bobfracsin * 0.005;
00932     angles[YAW] += scale * cg.bobfracsin * 0.01;
00933     angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005;
00934 
00935     // drop the weapon when landing
00936     delta = cg.time - cg.landTime;
00937     if ( delta < LAND_DEFLECT_TIME ) {
00938         origin[2] += cg.landChange*0.25 * delta / LAND_DEFLECT_TIME;
00939     } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
00940         origin[2] += cg.landChange*0.25 * 
00941             (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME;
00942     }
00943 
00944 #if 0
00945     // drop the weapon when stair climbing
00946     delta = cg.time - cg.stepTime;
00947     if ( delta < STEP_TIME/2 ) {
00948         origin[2] -= cg.stepChange*0.25 * delta / (STEP_TIME/2);
00949     } else if ( delta < STEP_TIME ) {
00950         origin[2] -= cg.stepChange*0.25 * (STEP_TIME - delta) / (STEP_TIME/2);
00951     }
00952 #endif
00953 
00954     // idle drift
00955     scale = cg.xyspeed + 40;
00956     fracsin = sin( cg.time * 0.001 );
00957     angles[ROLL] += scale * fracsin * 0.01;
00958     angles[YAW] += scale * fracsin * 0.01;
00959     angles[PITCH] += scale * fracsin * 0.01;
00960 }
00961 
00962 
00963 /*
00964 ===============
00965 CG_LightningBolt
00966 
00967 Origin will be the exact tag point, which is slightly
00968 different than the muzzle point used for determining hits.
00969 The cent should be the non-predicted cent if it is from the player,
00970 so the endpoint will reflect the simulated strike (lagging the predicted
00971 angle)
00972 ===============
00973 */
00974 static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
00975     trace_t  trace;
00976     refEntity_t  beam;
00977     vec3_t   forward;
00978     vec3_t   muzzlePoint, endPoint;
00979 
00980     if (cent->currentState.weapon != WP_LIGHTNING) {
00981         return;
00982     }
00983 
00984     memset( &beam, 0, sizeof( beam ) );
00985 
00986     // CPMA  "true" lightning
00987     if ((cent->currentState.number == cg.predictedPlayerState.clientNum) && (cg_trueLightning.value != 0)) {
00988         vec3_t angle;
00989         int i;
00990 
00991         for (i = 0; i < 3; i++) {
00992             float a = cent->lerpAngles[i] - cg.refdefViewAngles[i];
00993             if (a > 180) {
00994                 a -= 360;
00995             }
00996             if (a < -180) {
00997                 a += 360;
00998             }
00999 
01000             angle[i] = cg.refdefViewAngles[i] + a * (1.0 - cg_trueLightning.value);
01001             if (angle[i] < 0) {
01002                 angle[i] += 360;
01003             }
01004             if (angle[i] > 360) {
01005                 angle[i] -= 360;
01006             }
01007         }
01008 
01009         AngleVectors(angle, forward, NULL, NULL );
01010         VectorCopy(cent->lerpOrigin, muzzlePoint );
01011 //      VectorCopy(cg.refdef.vieworg, muzzlePoint );
01012     } else {
01013         // !CPMA
01014         AngleVectors( cent->lerpAngles, forward, NULL, NULL );
01015         VectorCopy(cent->lerpOrigin, muzzlePoint );
01016     }
01017 
01018     // FIXME: crouch
01019     muzzlePoint[2] += DEFAULT_VIEWHEIGHT;
01020 
01021     VectorMA( muzzlePoint, 14, forward, muzzlePoint );
01022 
01023     // project forward by the lightning range
01024     VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint );
01025 
01026     // see if it hit a wall
01027     CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, 
01028         cent->currentState.number, MASK_SHOT );
01029 
01030     // this is the endpoint
01031     VectorCopy( trace.endpos, beam.oldorigin );
01032 
01033     // use the provided origin, even though it may be slightly
01034     // different than the muzzle origin
01035     VectorCopy( origin, beam.origin );
01036 
01037     beam.reType = RT_LIGHTNING;
01038     beam.customShader = cgs.media.lightningShader;
01039     trap_R_AddRefEntityToScene( &beam );
01040 
01041     // add the impact flare if it hit something
01042     if ( trace.fraction < 1.0 ) {
01043         vec3_t  angles;
01044         vec3_t  dir;
01045 
01046         VectorSubtract( beam.oldorigin, beam.origin, dir );
01047         VectorNormalize( dir );
01048 
01049         memset( &beam, 0, sizeof( beam ) );
01050         beam.hModel = cgs.media.lightningExplosionModel;
01051 
01052         VectorMA( trace.endpos, -16, dir, beam.origin );
01053 
01054         // make a random orientation
01055         angles[0] = rand() % 360;
01056         angles[1] = rand() % 360;
01057         angles[2] = rand() % 360;
01058         AnglesToAxis( angles, beam.axis );
01059         trap_R_AddRefEntityToScene( &beam );
01060     }
01061 }
01062 /*
01063 
01064 static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
01065     trace_t     trace;
01066     refEntity_t     beam;
01067     vec3_t          forward;
01068     vec3_t          muzzlePoint, endPoint;
01069 
01070     if ( cent->currentState.weapon != WP_LIGHTNING ) {
01071         return;
01072     }
01073 
01074     memset( &beam, 0, sizeof( beam ) );
01075 
01076     // find muzzle point for this frame
01077     VectorCopy( cent->lerpOrigin, muzzlePoint );
01078     AngleVectors( cent->lerpAngles, forward, NULL, NULL );
01079 
01080     // FIXME: crouch
01081     muzzlePoint[2] += DEFAULT_VIEWHEIGHT;
01082 
01083     VectorMA( muzzlePoint, 14, forward, muzzlePoint );
01084 
01085     // project forward by the lightning range
01086     VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint );
01087 
01088     // see if it hit a wall
01089     CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, 
01090         cent->currentState.number, MASK_SHOT );
01091 
01092     // this is the endpoint
01093     VectorCopy( trace.endpos, beam.oldorigin );
01094 
01095     // use the provided origin, even though it may be slightly
01096     // different than the muzzle origin
01097     VectorCopy( origin, beam.origin );
01098 
01099     beam.reType = RT_LIGHTNING;
01100     beam.customShader = cgs.media.lightningShader;
01101     trap_R_AddRefEntityToScene( &beam );
01102 
01103     // add the impact flare if it hit something
01104     if ( trace.fraction < 1.0 ) {
01105         vec3_t  angles;
01106         vec3_t  dir;
01107 
01108         VectorSubtract( beam.oldorigin, beam.origin, dir );
01109         VectorNormalize( dir );
01110 
01111         memset( &beam, 0, sizeof( beam ) );
01112         beam.hModel = cgs.media.lightningExplosionModel;
01113 
01114         VectorMA( trace.endpos, -16, dir, beam.origin );
01115 
01116         // make a random orientation
01117         angles[0] = rand() % 360;
01118         angles[1] = rand() % 360;
01119         angles[2] = rand() % 360;
01120         AnglesToAxis( angles, beam.axis );
01121         trap_R_AddRefEntityToScene( &beam );
01122     }
01123 }
01124 */
01125 
01126 /*
01127 ===============
01128 CG_SpawnRailTrail
01129 
01130 Origin will be the exact tag point, which is slightly
01131 different than the muzzle point used for determining hits.
01132 ===============
01133 */
01134 static void CG_SpawnRailTrail( centity_t *cent, vec3_t origin ) {
01135     clientInfo_t    *ci;
01136 
01137     if ( cent->currentState.weapon != WP_RAILGUN ) {
01138         return;
01139     }
01140     if ( !cent->pe.railgunFlash ) {
01141         return;
01142     }
01143     cent->pe.railgunFlash = qtrue;
01144     ci = &cgs.clientinfo[ cent->currentState.clientNum ];
01145     CG_RailTrail( ci, origin, cent->pe.railgunImpact );
01146 }
01147 
01148 
01149 /*
01150 ======================
01151 CG_MachinegunSpinAngle
01152 ======================
01153 */
01154 #define     SPIN_SPEED  0.9
01155 #define     COAST_TIME  1000
01156 static float    CG_MachinegunSpinAngle( centity_t *cent ) {
01157     int     delta;
01158     float   angle;
01159     float   speed;
01160 
01161     delta = cg.time - cent->pe.barrelTime;
01162     if ( cent->pe.barrelSpinning ) {
01163         angle = cent->pe.barrelAngle + delta * SPIN_SPEED;
01164     } else {
01165         if ( delta > COAST_TIME ) {
01166             delta = COAST_TIME;
01167         }
01168 
01169         speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
01170         angle = cent->pe.barrelAngle + delta * speed;
01171     }
01172 
01173     if ( cent->pe.barrelSpinning == !(cent->currentState.eFlags & EF_FIRING) ) {
01174         cent->pe.barrelTime = cg.time;
01175         cent->pe.barrelAngle = AngleMod( angle );
01176         cent->pe.barrelSpinning = !!(cent->currentState.eFlags & EF_FIRING);
01177 #ifdef MISSIONPACK
01178         if ( cent->currentState.weapon == WP_CHAINGUN && !cent->pe.barrelSpinning ) {
01179             trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, trap_S_RegisterSound( "sound/weapons/vulcan/wvulwind.wav", qfalse ) );
01180         }
01181 #endif
01182     }
01183 
01184     return angle;
01185 }
01186 
01187 
01188 /*
01189 ========================
01190 CG_AddWeaponWithPowerups
01191 ========================
01192 */
01193 static void CG_AddWeaponWithPowerups( refEntity_t *gun, int powerups ) {
01194     // add powerup effects
01195     if ( powerups & ( 1 << PW_INVIS ) ) {
01196         gun->customShader = cgs.media.invisShader;
01197         trap_R_AddRefEntityToScene( gun );
01198     } else {
01199         trap_R_AddRefEntityToScene( gun );
01200 
01201         if ( powerups & ( 1 << PW_BATTLESUIT ) ) {
01202             gun->customShader = cgs.media.battleWeaponShader;
01203             trap_R_AddRefEntityToScene( gun );
01204         }
01205         if ( powerups & ( 1 << PW_QUAD ) ) {
01206             gun->customShader = cgs.media.quadWeaponShader;
01207             trap_R_AddRefEntityToScene( gun );
01208         }
01209     }
01210 }
01211 
01212 
01213 /*
01214 =============
01215 CG_AddPlayerWeapon
01216 
01217 Used for both the view weapon (ps is valid) and the world modelother character models (ps is NULL)
01218 The main player will have this called for BOTH cases, so effects like ligh