00001
00002
00003
00004
00005
00006 #include "g_local.h"
00007 #include "be_aas.h"
00008 #include "bg_saga.h"
00009 #include "../ghoul2/G2.h"
00010 #include "q_shared.h"
00011
00012 static float s_quadFactor;
00013 static vec3_t forward, vright, up;
00014 static vec3_t muzzle;
00015
00016
00017
00018 #define BRYAR_PISTOL_VEL 1600
00019 #define BRYAR_PISTOL_DAMAGE 10
00020 #define BRYAR_CHARGE_UNIT 200.0f // bryar charging gives us one more unit every 200ms--if you change this, you'll have to do the same in bg_pmove
00021 #define BRYAR_ALT_SIZE 1.0f
00022
00023
00024
00025 #define BLASTER_SPREAD 1.6f//1.2f
00026 #define BLASTER_VELOCITY 2300
00027 #define BLASTER_DAMAGE 20
00028
00029
00030
00031 #define DISRUPTOR_MAIN_DAMAGE 30 //40
00032 #define DISRUPTOR_MAIN_DAMAGE_SIEGE 50
00033 #define DISRUPTOR_NPC_MAIN_DAMAGE_CUT 0.25f
00034
00035 #define DISRUPTOR_ALT_DAMAGE 100 //125
00036 #define DISRUPTOR_NPC_ALT_DAMAGE_CUT 0.2f
00037 #define DISRUPTOR_ALT_TRACES 3 // can go through a max of 3 damageable(sp?) entities
00038 #define DISRUPTOR_CHARGE_UNIT 50.0f // distruptor charging gives us one more unit every 50ms--if you change this, you'll have to do the same in bg_pmove
00039
00040
00041
00042 #define BOWCASTER_DAMAGE 50
00043 #define BOWCASTER_VELOCITY 1300
00044 #define BOWCASTER_SPLASH_DAMAGE 0
00045 #define BOWCASTER_SPLASH_RADIUS 0
00046 #define BOWCASTER_SIZE 2
00047
00048 #define BOWCASTER_ALT_SPREAD 5.0f
00049 #define BOWCASTER_VEL_RANGE 0.3f
00050 #define BOWCASTER_CHARGE_UNIT 200.0f // bowcaster charging gives us one more unit every 200ms--if you change this, you'll have to do the same in bg_pmove
00051
00052
00053
00054 #define REPEATER_SPREAD 1.4f
00055 #define REPEATER_DAMAGE 14
00056 #define REPEATER_VELOCITY 1600
00057
00058 #define REPEATER_ALT_SIZE 3 // half of bbox size
00059 #define REPEATER_ALT_DAMAGE 60
00060 #define REPEATER_ALT_SPLASH_DAMAGE 60
00061 #define REPEATER_ALT_SPLASH_RADIUS 128
00062 #define REPEATER_ALT_SPLASH_RAD_SIEGE 80
00063 #define REPEATER_ALT_VELOCITY 1100
00064
00065
00066
00067 #define DEMP2_DAMAGE 35
00068 #define DEMP2_VELOCITY 1800
00069 #define DEMP2_SIZE 2 // half of bbox size
00070
00071 #define DEMP2_ALT_DAMAGE 8 //12 // does 12, 36, 84 at each of the 3 charge levels.
00072 #define DEMP2_CHARGE_UNIT 700.0f // demp2 charging gives us one more unit every 700ms--if you change this, you'll have to do the same in bg_weapons
00073 #define DEMP2_ALT_RANGE 4096
00074 #define DEMP2_ALT_SPLASHRADIUS 256
00075
00076
00077
00078 #define FLECHETTE_SHOTS 5
00079 #define FLECHETTE_SPREAD 4.0f
00080 #define FLECHETTE_DAMAGE 12//15
00081 #define FLECHETTE_VEL 3500
00082 #define FLECHETTE_SIZE 1
00083 #define FLECHETTE_MINE_RADIUS_CHECK 256
00084 #define FLECHETTE_ALT_DAMAGE 60
00085 #define FLECHETTE_ALT_SPLASH_DAM 60
00086 #define FLECHETTE_ALT_SPLASH_RAD 128
00087
00088
00089
00090 #define ROCKET_VELOCITY 900
00091 #define ROCKET_DAMAGE 100
00092 #define ROCKET_SPLASH_DAMAGE 100
00093 #define ROCKET_SPLASH_RADIUS 160
00094 #define ROCKET_SIZE 3
00095 #define ROCKET_ALT_THINK_TIME 100
00096
00097
00098
00099
00100
00101
00102 #define CONC_VELOCITY 3000
00103 #define CONC_DAMAGE 75 //150
00104 #define CONC_NPC_DAMAGE_EASY 40
00105 #define CONC_NPC_DAMAGE_NORMAL 80
00106 #define CONC_NPC_DAMAGE_HARD 100
00107 #define CONC_SPLASH_DAMAGE 40 //50
00108 #define CONC_SPLASH_RADIUS 200 //300
00109
00110 #define CONC_ALT_DAMAGE 25 //100
00111 #define CONC_ALT_NPC_DAMAGE_EASY 20
00112 #define CONC_ALT_NPC_DAMAGE_MEDIUM 35
00113 #define CONC_ALT_NPC_DAMAGE_HARD 50
00114
00115
00116
00117 #define STUN_BATON_DAMAGE 20
00118 #define STUN_BATON_ALT_DAMAGE 20
00119 #define STUN_BATON_RANGE 8
00120
00121
00122
00123 #define MELEE_SWING1_DAMAGE 10
00124 #define MELEE_SWING2_DAMAGE 12
00125 #define MELEE_RANGE 8
00126
00127
00128
00129 #define ATST_MAIN_VEL 4000 //
00130 #define ATST_MAIN_DAMAGE 25 //
00131 #define ATST_MAIN_SIZE 3 // make it easier to hit things
00132
00133
00134
00135 #define ATST_SIDE_MAIN_DAMAGE 75
00136 #define ATST_SIDE_MAIN_VELOCITY 1300
00137 #define ATST_SIDE_MAIN_NPC_DAMAGE_EASY 30
00138 #define ATST_SIDE_MAIN_NPC_DAMAGE_NORMAL 40
00139 #define ATST_SIDE_MAIN_NPC_DAMAGE_HARD 50
00140 #define ATST_SIDE_MAIN_SIZE 4
00141 #define ATST_SIDE_MAIN_SPLASH_DAMAGE 10 // yeah, pretty small, either zero out or make it worth having?
00142 #define ATST_SIDE_MAIN_SPLASH_RADIUS 16 // yeah, pretty small, either zero out or make it worth having?
00143
00144 #define ATST_SIDE_ALT_VELOCITY 1100
00145 #define ATST_SIDE_ALT_NPC_VELOCITY 600
00146 #define ATST_SIDE_ALT_DAMAGE 130
00147
00148 #define ATST_SIDE_ROCKET_NPC_DAMAGE_EASY 30
00149 #define ATST_SIDE_ROCKET_NPC_DAMAGE_NORMAL 50
00150 #define ATST_SIDE_ROCKET_NPC_DAMAGE_HARD 90
00151
00152 #define ATST_SIDE_ALT_SPLASH_DAMAGE 130
00153 #define ATST_SIDE_ALT_SPLASH_RADIUS 200
00154 #define ATST_SIDE_ALT_ROCKET_SIZE 5
00155 #define ATST_SIDE_ALT_ROCKET_SPLASH_SCALE 0.5f // scales splash for NPC's
00156
00157 extern qboolean G_BoxInBounds( vec3_t point, vec3_t mins, vec3_t maxs, vec3_t boundsMins, vec3_t boundsMaxs );
00158 extern qboolean G_HeavyMelee( gentity_t *attacker );
00159 extern void Jedi_Decloak( gentity_t *self );
00160
00161 static void WP_FireEmplaced( gentity_t *ent, qboolean altFire );
00162
00163 void laserTrapStick( gentity_t *ent, vec3_t endpos, vec3_t normal );
00164
00165 void touch_NULL( gentity_t *ent, gentity_t *other, trace_t *trace )
00166 {
00167
00168 }
00169
00170 void laserTrapExplode( gentity_t *self );
00171 void RocketDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod);
00172
00173
00174 float WP_SpeedOfMissileForWeapon( int wp, qboolean alt_fire )
00175 {
00176 return 500;
00177 }
00178
00179
00180 void W_TraceSetStart( gentity_t *ent, vec3_t start, vec3_t mins, vec3_t maxs )
00181
00182 {
00183
00184 trace_t tr;
00185 vec3_t entMins;
00186 vec3_t entMaxs;
00187 vec3_t eyePoint;
00188
00189 VectorAdd( ent->r.currentOrigin, ent->r.mins, entMins );
00190 VectorAdd( ent->r.currentOrigin, ent->r.maxs, entMaxs );
00191
00192 if ( G_BoxInBounds( start, mins, maxs, entMins, entMaxs ) )
00193 {
00194 return;
00195 }
00196
00197 if ( !ent->client )
00198 {
00199 return;
00200 }
00201
00202 VectorCopy( ent->s.pos.trBase, eyePoint);
00203 eyePoint[2] += ent->client->ps.viewheight;
00204
00205 trap_Trace( &tr, eyePoint, mins, maxs, start, ent->s.number, MASK_SOLID|CONTENTS_SHOTCLIP );
00206
00207 if ( tr.startsolid || tr.allsolid )
00208 {
00209 return;
00210 }
00211
00212 if ( tr.fraction < 1.0f )
00213 {
00214 VectorCopy( tr.endpos, start );
00215 }
00216 }
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234 static void WP_FireBryarPistol( gentity_t *ent, qboolean altFire )
00235
00236 {
00237 int damage = BRYAR_PISTOL_DAMAGE;
00238 int count;
00239
00240 gentity_t *missile = CreateMissile( muzzle, forward, BRYAR_PISTOL_VEL, 10000, ent, altFire );
00241
00242 missile->classname = "bryar_proj";
00243 missile->s.weapon = WP_BRYAR_PISTOL;
00244
00245 if ( altFire )
00246 {
00247 float boxSize = 0;
00248
00249 count = ( level.time - ent->client->ps.weaponChargeTime ) / BRYAR_CHARGE_UNIT;
00250
00251 if ( count < 1 )
00252 {
00253 count = 1;
00254 }
00255 else if ( count > 5 )
00256 {
00257 count = 5;
00258 }
00259
00260 if (count > 1)
00261 {
00262 damage *= (count*1.7);
00263 }
00264 else
00265 {
00266 damage *= (count*1.5);
00267 }
00268
00269 missile->s.generic1 = count;
00270
00271 boxSize = BRYAR_ALT_SIZE*(count*0.5);
00272
00273 VectorSet( missile->r.maxs, boxSize, boxSize, boxSize );
00274 VectorSet( missile->r.mins, -boxSize, -boxSize, -boxSize );
00275 }
00276
00277 missile->damage = damage;
00278 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00279 if (altFire)
00280 {
00281 missile->methodOfDeath = MOD_BRYAR_PISTOL_ALT;
00282 }
00283 else
00284 {
00285 missile->methodOfDeath = MOD_BRYAR_PISTOL;
00286 }
00287 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00288
00289
00290 missile->bounceCount = 8;
00291 }
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302 void WP_FireTurretMissile( gentity_t *ent, vec3_t start, vec3_t dir, qboolean altFire, int damage, int velocity, int mod, gentity_t *ignore )
00303
00304 {
00305 gentity_t *missile;
00306
00307 missile = CreateMissile( start, dir, velocity, 10000, ent, altFire );
00308
00309 missile->classname = "generic_proj";
00310 missile->s.weapon = WP_TURRET;
00311
00312 missile->damage = damage;
00313 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00314 missile->methodOfDeath = mod;
00315 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00316
00317 if (ignore)
00318 {
00319 missile->passThroughNum = ignore->s.number+1;
00320 }
00321
00322
00323 missile->bounceCount = 8;
00324 }
00325
00326
00327
00328
00329 void WP_FireGenericBlasterMissile( gentity_t *ent, vec3_t start, vec3_t dir, qboolean altFire, int damage, int velocity, int mod )
00330
00331 {
00332 gentity_t *missile;
00333
00334 missile = CreateMissile( start, dir, velocity, 10000, ent, altFire );
00335
00336 missile->classname = "generic_proj";
00337 missile->s.weapon = WP_BRYAR_PISTOL;
00338
00339 missile->damage = damage;
00340 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00341 missile->methodOfDeath = mod;
00342 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00343
00344
00345 missile->bounceCount = 8;
00346 }
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357 void WP_FireBlasterMissile( gentity_t *ent, vec3_t start, vec3_t dir, qboolean altFire )
00358
00359 {
00360 int velocity = BLASTER_VELOCITY;
00361 int damage = BLASTER_DAMAGE;
00362 gentity_t *missile;
00363
00364 if (ent->s.eType == ET_NPC)
00365 {
00366 damage = 10;
00367 }
00368
00369 missile = CreateMissile( start, dir, velocity, 10000, ent, altFire );
00370
00371 missile->classname = "blaster_proj";
00372 missile->s.weapon = WP_BLASTER;
00373
00374 missile->damage = damage;
00375 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00376 missile->methodOfDeath = MOD_BLASTER;
00377 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00378
00379
00380 missile->bounceCount = 8;
00381 }
00382
00383
00384 void WP_FireTurboLaserMissile( gentity_t *ent, vec3_t start, vec3_t dir )
00385
00386 {
00387 int velocity = ent->mass;
00388 gentity_t *missile;
00389
00390 missile = CreateMissile( start, dir, velocity, 10000, ent, qfalse );
00391
00392
00393 missile->s.otherEntityNum2 = ent->genericValue14;
00394
00395 missile->s.emplacedOwner = ent->genericValue15;
00396
00397 missile->classname = "turbo_proj";
00398 missile->s.weapon = WP_TURRET;
00399
00400 missile->damage = ent->damage;
00401 missile->splashDamage = ent->splashDamage;
00402 missile->splashRadius = ent->splashRadius;
00403 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00404 missile->methodOfDeath = MOD_TURBLAST;
00405 missile->splashMethodOfDeath = MOD_TURBLAST;
00406 missile->clipmask = MASK_SHOT;
00407
00408
00409 missile->bounceCount = 8;
00410
00411
00412 missile->s.owner = ent->s.number;
00413
00414
00415 missile->think = G_FreeEntity;
00416 missile->nextthink = level.time + 5000;
00417 }
00418
00419
00420 void WP_FireEmplacedMissile( gentity_t *ent, vec3_t start, vec3_t dir, qboolean altFire, gentity_t *ignore )
00421
00422 {
00423 int velocity = BLASTER_VELOCITY;
00424 int damage = BLASTER_DAMAGE;
00425 gentity_t *missile;
00426
00427 missile = CreateMissile( start, dir, velocity, 10000, ent, altFire );
00428
00429 missile->classname = "emplaced_gun_proj";
00430 missile->s.weapon = WP_TURRET;
00431
00432 missile->activator = ignore;
00433
00434 missile->damage = damage;
00435 missile->dflags = (DAMAGE_DEATH_KNOCKBACK|DAMAGE_HEAVY_WEAP_CLASS);
00436 missile->methodOfDeath = MOD_VEHICLE;
00437 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00438
00439 if (ignore)
00440 {
00441 missile->passThroughNum = ignore->s.number+1;
00442 }
00443
00444
00445 missile->bounceCount = 8;
00446 }
00447
00448
00449 static void WP_FireBlaster( gentity_t *ent, qboolean altFire )
00450
00451 {
00452 vec3_t dir, angs;
00453
00454 vectoangles( forward, angs );
00455
00456 if ( altFire )
00457 {
00458
00459 angs[PITCH] += crandom() * BLASTER_SPREAD;
00460 angs[YAW] += crandom() * BLASTER_SPREAD;
00461 }
00462
00463 AngleVectors( angs, dir, NULL, NULL );
00464
00465
00466 WP_FireBlasterMissile( ent, muzzle, dir, altFire );
00467 }
00468
00469
00470
00471 int G_GetHitLocation(gentity_t *target, vec3_t ppoint);
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481 static void WP_DisruptorMainFire( gentity_t *ent )
00482
00483 {
00484 int damage = DISRUPTOR_MAIN_DAMAGE;
00485 qboolean render_impact = qtrue;
00486 vec3_t start, end;
00487 trace_t tr;
00488 gentity_t *traceEnt, *tent;
00489 float shotRange = 8192;
00490 int ignore, traces;
00491
00492 if ( g_gametype.integer == GT_SIEGE )
00493 {
00494 damage = DISRUPTOR_MAIN_DAMAGE_SIEGE;
00495 }
00496
00497 memset(&tr, 0, sizeof(tr));
00498
00499 VectorCopy( ent->client->ps.origin, start );
00500 start[2] += ent->client->ps.viewheight;
00501
00502 VectorMA( start, shotRange, forward, end );
00503
00504 ignore = ent->s.number;
00505 traces = 0;
00506 while ( traces < 10 )
00507 {
00508 if (d_projectileGhoul2Collision.integer)
00509 {
00510 trap_G2Trace( &tr, start, NULL, NULL, end, ignore, MASK_SHOT, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer );
00511 }
00512 else
00513 {
00514 trap_Trace( &tr, start, NULL, NULL, end, ignore, MASK_SHOT );
00515 }
00516
00517 traceEnt = &g_entities[tr.entityNum];
00518
00519 if (d_projectileGhoul2Collision.integer && traceEnt->inuse && traceEnt->client)
00520 {
00521 if (traceEnt->inuse && traceEnt->client && traceEnt->ghoul2)
00522 {
00523 traceEnt->client->g2LastSurfaceHit = tr.surfaceFlags;
00524 traceEnt->client->g2LastSurfaceTime = level.time;
00525 }
00526
00527 if (traceEnt->ghoul2)
00528 {
00529 tr.surfaceFlags = 0;
00530 }
00531 }
00532
00533 if (traceEnt && traceEnt->client && traceEnt->client->ps.duelInProgress &&
00534 traceEnt->client->ps.duelIndex != ent->s.number)
00535 {
00536 VectorCopy( tr.endpos, start );
00537 ignore = tr.entityNum;
00538 traces++;
00539 continue;
00540 }
00541
00542 if ( Jedi_DodgeEvasion( traceEnt, ent, &tr, G_GetHitLocation(traceEnt, tr.endpos) ) )
00543 {
00544 VectorCopy( tr.endpos, start );
00545 ignore = tr.entityNum;
00546 traces++;
00547 continue;
00548 }
00549 else if (traceEnt && traceEnt->client && traceEnt->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] >= FORCE_LEVEL_3)
00550 {
00551 if (WP_SaberCanBlock(traceEnt, tr.endpos, 0, MOD_DISRUPTOR, qtrue, 0))
00552 {
00553 gentity_t *te = NULL;
00554
00555 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_MAIN_SHOT );
00556 VectorCopy( muzzle, tent->s.origin2 );
00557 tent->s.eventParm = ent->s.number;
00558
00559 te = G_TempEntity( tr.endpos, EV_SABER_BLOCK );
00560 VectorCopy(tr.endpos, te->s.origin);
00561 VectorCopy(tr.plane.normal, te->s.angles);
00562 if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
00563 {
00564 te->s.angles[1] = 1;
00565 }
00566 te->s.eventParm = 0;
00567 te->s.weapon = 0;
00568 te->s.legsAnim = 0;
00569
00570 return;
00571 }
00572 }
00573 else if ( (traceEnt->flags&FL_SHIELDED) )
00574 {
00575 return;
00576 }
00577
00578 break;
00579 }
00580
00581 if ( tr.surfaceFlags & SURF_NOIMPACT )
00582 {
00583 render_impact = qfalse;
00584 }
00585
00586
00587 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_MAIN_SHOT );
00588 VectorCopy( muzzle, tent->s.origin2 );
00589 tent->s.eventParm = ent->s.number;
00590
00591 traceEnt = &g_entities[tr.entityNum];
00592
00593 if ( render_impact )
00594 {
00595 if ( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage )
00596 {
00597 if ( traceEnt->client && LogAccuracyHit( traceEnt, ent ))
00598 {
00599 ent->client->accuracy_hits++;
00600 }
00601
00602 G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NORMAL, MOD_DISRUPTOR );
00603
00604 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_HIT );
00605 tent->s.eventParm = DirToByte( tr.plane.normal );
00606 if (traceEnt->client)
00607 {
00608 tent->s.weapon = 1;
00609 }
00610 }
00611 else
00612 {
00613
00614 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_MISS );
00615 tent->s.eventParm = DirToByte( tr.plane.normal );
00616 tent->s.weapon = 1;
00617 }
00618 }
00619 }
00620
00621
00622 qboolean G_CanDisruptify(gentity_t *ent)
00623 {
00624 if (!ent || !ent->inuse || !ent->client || ent->s.eType != ET_NPC ||
00625 ent->s.NPC_class != CLASS_VEHICLE || !ent->m_pVehicle)
00626 {
00627 return qtrue;
00628 }
00629
00630 if (ent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL)
00631 {
00632 return qtrue;
00633 }
00634
00635
00636 return qfalse;
00637 }
00638
00639
00640 void WP_DisruptorAltFire( gentity_t *ent )
00641
00642 {
00643 int damage = 0, skip;
00644 qboolean render_impact = qtrue;
00645 vec3_t start, end;
00646 vec3_t muzzle2;
00647 trace_t tr;
00648 gentity_t *traceEnt, *tent;
00649 float shotRange = 8192.0f;
00650 int i;
00651 int count, maxCount = 60;
00652 int traces = DISRUPTOR_ALT_TRACES;
00653 qboolean fullCharge = qfalse;
00654
00655 damage = DISRUPTOR_ALT_DAMAGE-30;
00656
00657 VectorCopy( muzzle, muzzle2 );
00658
00659 if (ent->client)
00660 {
00661 VectorCopy( ent->client->ps.origin, start );
00662 start[2] += ent->client->ps.viewheight;
00663
00664 count = ( level.time - ent->client->ps.weaponChargeTime ) / DISRUPTOR_CHARGE_UNIT;
00665 if ( g_gametype.integer == GT_SIEGE )
00666 {
00667
00668 maxCount = 200;
00669 }
00670 }
00671 else
00672 {
00673 VectorCopy( ent->r.currentOrigin, start );
00674 start[2] += 24;
00675
00676 count = ( 100 ) / DISRUPTOR_CHARGE_UNIT;
00677 }
00678
00679 count *= 2;
00680
00681 if ( count < 1 )
00682 {
00683 count = 1;
00684 }
00685 else if ( count >= maxCount )
00686 {
00687 count = maxCount;
00688 fullCharge = qtrue;
00689 }
00690
00691
00692 if ( count < 10 )
00693 {
00694 traces = 1;
00695 }
00696 else if ( count < 20 )
00697 {
00698 traces = 2;
00699 }
00700
00701 damage += count;
00702
00703 skip = ent->s.number;
00704
00705 for (i = 0; i < traces; i++ )
00706 {
00707 VectorMA( start, shotRange, forward, end );
00708
00709 if (d_projectileGhoul2Collision.integer)
00710 {
00711 trap_G2Trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer );
00712 }
00713 else
00714 {
00715 trap_Trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT );
00716 }
00717
00718 traceEnt = &g_entities[tr.entityNum];
00719
00720 if (d_projectileGhoul2Collision.integer && traceEnt->inuse && traceEnt->client)
00721 {
00722 if (traceEnt->inuse && traceEnt->client && traceEnt->ghoul2)
00723 {
00724 traceEnt->client->g2LastSurfaceHit = tr.surfaceFlags;
00725 traceEnt->client->g2LastSurfaceTime = level.time;
00726 }
00727
00728 if (traceEnt->ghoul2)
00729 {
00730 tr.surfaceFlags = 0;
00731 }
00732 }
00733
00734 if ( tr.surfaceFlags & SURF_NOIMPACT )
00735 {
00736 render_impact = qfalse;
00737 }
00738
00739 if (traceEnt && traceEnt->client && traceEnt->client->ps.duelInProgress &&
00740 traceEnt->client->ps.duelIndex != ent->s.number)
00741 {
00742 skip = tr.entityNum;
00743 VectorCopy(tr.endpos, start);
00744 continue;
00745 }
00746
00747 if (Jedi_DodgeEvasion(traceEnt, ent, &tr, G_GetHitLocation(traceEnt, tr.endpos)))
00748 {
00749 skip = tr.entityNum;
00750 VectorCopy(tr.endpos, start);
00751 continue;
00752 }
00753 else if (traceEnt && traceEnt->client && traceEnt->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] >= FORCE_LEVEL_3)
00754 {
00755 if (WP_SaberCanBlock(traceEnt, tr.endpos, 0, MOD_DISRUPTOR_SNIPER, qtrue, 0))
00756 {
00757 gentity_t *te = NULL;
00758
00759 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT );
00760 VectorCopy( muzzle, tent->s.origin2 );
00761 tent->s.shouldtarget = fullCharge;
00762 tent->s.eventParm = ent->s.number;
00763
00764 te = G_TempEntity( tr.endpos, EV_SABER_BLOCK );
00765 VectorCopy(tr.endpos, te->s.origin);
00766 VectorCopy(tr.plane.normal, te->s.angles);
00767 if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
00768 {
00769 te->s.angles[1] = 1;
00770 }
00771 te->s.eventParm = 0;
00772 te->s.weapon = 0;
00773 te->s.legsAnim = 0;
00774
00775 return;
00776 }
00777 }
00778
00779
00780 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT );
00781 VectorCopy( muzzle, tent->s.origin2 );
00782 tent->s.shouldtarget = fullCharge;
00783 tent->s.eventParm = ent->s.number;
00784
00785
00786 if ( render_impact )
00787 {
00788 if ( traceEnt->takedamage && traceEnt->client )
00789 {
00790 tent->s.otherEntityNum = traceEnt->s.number;
00791
00792
00793 tent = G_TempEntity(tr.endpos, EV_MISSILE_MISS);
00794 tent->s.eventParm = DirToByte(tr.plane.normal);
00795 tent->s.eFlags |= EF_ALT_FIRING;
00796
00797 if ( LogAccuracyHit( traceEnt, ent ))
00798 {
00799 if (ent->client)
00800 {
00801 ent->client->accuracy_hits++;
00802 }
00803 }
00804 }
00805 else
00806 {
00807 if ( traceEnt->r.svFlags & SVF_GLASS_BRUSH
00808 || traceEnt->takedamage
00809 || traceEnt->s.eType == ET_MOVER )
00810 {
00811 if ( traceEnt->takedamage )
00812 {
00813 G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage,
00814 DAMAGE_NO_KNOCKBACK, MOD_DISRUPTOR_SNIPER );
00815
00816 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_HIT );
00817 tent->s.eventParm = DirToByte( tr.plane.normal );
00818 }
00819 }
00820 else
00821 {
00822
00823 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_MISS );
00824 tent->s.eventParm = DirToByte( tr.plane.normal );
00825 }
00826 break;
00827 }
00828
00829 if ( (traceEnt->flags&FL_SHIELDED) )
00830 {
00831 break;
00832 }
00833
00834 if ( traceEnt->takedamage )
00835 {
00836 vec3_t preAng;
00837 int preHealth = traceEnt->health;
00838 int preLegs = 0;
00839 int preTorso = 0;
00840
00841 if (traceEnt->client)
00842 {
00843 preLegs = traceEnt->client->ps.legsAnim;
00844 preTorso = traceEnt->client->ps.torsoAnim;
00845 VectorCopy(traceEnt->client->ps.viewangles, preAng);
00846 }
00847
00848 G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_DISRUPTOR_SNIPER );
00849
00850 if (traceEnt->client && preHealth > 0 && traceEnt->health <= 0 && fullCharge &&
00851 G_CanDisruptify(traceEnt))
00852 {
00853 VectorCopy(preAng, traceEnt->client->ps.viewangles);
00854
00855 traceEnt->client->ps.eFlags |= EF_DISINTEGRATION;
00856 VectorCopy(tr.endpos, traceEnt->client->ps.lastHitLoc);
00857
00858 traceEnt->client->ps.legsAnim = preLegs;
00859 traceEnt->client->ps.torsoAnim = preTorso;
00860
00861 traceEnt->r.contents = 0;
00862
00863 VectorClear(traceEnt->client->ps.velocity);
00864 }
00865
00866 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_HIT );
00867 tent->s.eventParm = DirToByte( tr.plane.normal );
00868 if (traceEnt->client)
00869 {
00870 tent->s.weapon = 1;
00871 }
00872 }
00873 }
00874 else
00875 {
00876 break;
00877 }
00878
00879
00880 VectorCopy( tr.endpos, muzzle );
00881 VectorCopy( tr.endpos, start );
00882 skip = tr.entityNum;
00883 }
00884 }
00885
00886
00887
00888 static void WP_FireDisruptor( gentity_t *ent, qboolean altFire )
00889
00890 {
00891 if (!ent || !ent->client || ent->client->ps.zoomMode != 1)
00892 {
00893 altFire = qfalse;
00894 }
00895
00896 if (ent && ent->s.eType == ET_NPC && !ent->client)
00897 {
00898 WP_DisruptorAltFire( ent );
00899 return;
00900 }
00901
00902 if ( altFire )
00903 {
00904 WP_DisruptorAltFire( ent );
00905 }
00906 else
00907 {
00908 WP_DisruptorMainFire( ent );
00909 }
00910 }
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921 static void WP_BowcasterAltFire( gentity_t *ent )
00922 {
00923 int damage = BOWCASTER_DAMAGE;
00924
00925 gentity_t *missile = CreateMissile( muzzle, forward, BOWCASTER_VELOCITY, 10000, ent, qfalse);
00926
00927 missile->classname = "bowcaster_proj";
00928 missile->s.weapon = WP_BOWCASTER;
00929
00930 VectorSet( missile->r.maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE );
00931 VectorScale( missile->r.maxs, -1, missile->r.mins );
00932
00933 missile->damage = damage;
00934 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00935 missile->methodOfDeath = MOD_BOWCASTER;
00936 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00937
00938 missile->flags |= FL_BOUNCE;
00939 missile->bounceCount = 3;
00940 }
00941
00942
00943 static void WP_BowcasterMainFire( gentity_t *ent )
00944
00945 {
00946 int damage = BOWCASTER_DAMAGE, count;
00947 float vel;
00948 vec3_t angs, dir;
00949 gentity_t *missile;
00950 int i;
00951
00952 if (!ent->client)
00953 {
00954 count = 1;
00955 }
00956 else
00957 {
00958 count = ( level.time - ent->client->ps.weaponChargeTime ) / BOWCASTER_CHARGE_UNIT;
00959 }
00960
00961 if ( count < 1 )
00962 {
00963 count = 1;
00964 }
00965 else if ( count > 5 )
00966 {
00967 count = 5;
00968 }
00969
00970 if ( !(count & 1 ))
00971 {
00972
00973 count--;
00974 }
00975
00976
00977 if (count <= 1)
00978 {
00979 damage = 50;
00980 }
00981 else if (count == 2)
00982 {
00983 damage = 45;
00984 }
00985 else if (count == 3)
00986 {
00987 damage = 40;
00988 }
00989 else if (count == 4)
00990 {
00991 damage = 35;
00992 }
00993 else
00994 {
00995 damage = 30;
00996 }
00997
00998 for (i = 0; i < count; i++ )
00999 {
01000
01001 vel = BOWCASTER_VELOCITY * ( crandom() * BOWCASTER_VEL_RANGE + 1.0f );
01002
01003 vectoangles( forward, angs );
01004
01005
01006 angs[PITCH] += crandom() * BOWCASTER_ALT_SPREAD * 0.2f;
01007 angs[YAW] += ((i+0.5f) * BOWCASTER_ALT_SPREAD - count * 0.5f * BOWCASTER_ALT_SPREAD );
01008
01009 AngleVectors( angs, dir, NULL, NULL );
01010
01011 missile = CreateMissile( muzzle, dir, vel, 10000, ent, qtrue );
01012
01013 missile->classname = "bowcaster_alt_proj";
01014 missile->s.weapon = WP_BOWCASTER;
01015
01016 VectorSet( missile->r.maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE );
01017 VectorScale( missile->r.maxs, -1, missile->r.mins );
01018
01019 missile->damage = damage;
01020 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01021 missile->methodOfDeath = MOD_BOWCASTER;
01022 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
01023
01024
01025 missile->bounceCount = 0;
01026 }
01027 }
01028
01029
01030 static void WP_FireBowcaster( gentity_t *ent, qboolean altFire )
01031
01032 {
01033 if ( altFire )
01034 {
01035 WP_BowcasterAltFire( ent );
01036 }
01037 else
01038 {
01039 WP_BowcasterMainFire( ent );
01040 }
01041 }
01042
01043
01044
01045
01046
01047
01048
01049
01050
01051
01052
01053
01054 static void WP_RepeaterMainFire( gentity_t *ent, vec3_t dir )
01055
01056 {
01057 int damage = REPEATER_DAMAGE;
01058
01059 gentity_t *missile = CreateMissile( muzzle, dir, REPEATER_VELOCITY, 10000, ent, qfalse );
01060
01061 missile->classname = "repeater_proj";
01062 missile->s.weapon = WP_REPEATER;
01063
01064 missile->damage = damage;
01065 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01066 missile->methodOfDeath = MOD_REPEATER;
01067 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
01068
01069
01070 missile->bounceCount = 8;
01071 }
01072
01073
01074 static void WP_RepeaterAltFire( gentity_t *ent )
01075
01076 {
01077 int damage = REPEATER_ALT_DAMAGE;
01078
01079 gentity_t *missile = CreateMissile( muzzle, forward, REPEATER_ALT_VELOCITY, 10000, ent, qtrue );
01080
01081 missile->classname = "repeater_alt_proj";
01082 missile->s.weapon = WP_REPEATER;
01083
01084 VectorSet( missile->r.maxs, REPEATER_ALT_SIZE, REPEATER_ALT_SIZE, REPEATER_ALT_SIZE );
01085 VectorScale( missile->r.maxs, -1, missile->r.mins );
01086 missile->s.pos.trType = TR_GRAVITY;
01087 missile->s.pos.trDelta[2] += 40.0f;
01088 missile->damage = damage;
01089 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01090 missile->methodOfDeath = MOD_REPEATER_ALT;
01091 missile->splashMethodOfDeath = MOD_REPEATER_ALT_SPLASH;
01092 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
01093 missile->splashDamage = REPEATER_ALT_SPLASH_DAMAGE;
01094 if ( g_gametype.integer == GT_SIEGE )
01095 {
01096 missile->splashRadius = REPEATER_ALT_SPLASH_RAD_SIEGE;
01097 }
01098 else
01099 {
01100 missile->splashRadius = REPEATER_ALT_SPLASH_RADIUS;
01101 }
01102
01103
01104 missile->bounceCount = 8;
01105 }
01106
01107
01108 static void WP_FireRepeater( gentity_t *ent, qboolean altFire )
01109
01110 {
01111 vec3_t dir, angs;
01112
01113 vectoangles( forward, angs );
01114
01115 if ( altFire )
01116 {
01117 WP_RepeaterAltFire( ent );
01118 }
01119 else
01120 {
01121
01122 angs[PITCH] += crandom() * REPEATER_SPREAD;
01123 angs[YAW] += crandom() * REPEATER_SPREAD;
01124
01125 AngleVectors( angs, dir, NULL, NULL );
01126
01127 WP_RepeaterMainFire( ent, dir );
01128 }
01129 }
01130
01131
01132
01133
01134
01135
01136
01137
01138
01139
01140 static void WP_DEMP2_MainFire( gentity_t *ent )
01141 {
01142 int damage = DEMP2_DAMAGE;
01143
01144 gentity_t *missile = CreateMissile( muzzle, forward, DEMP2_VELOCITY, 10000, ent, qfalse);
01145
01146 missile->classname = "demp2_proj";
01147 missile->s.weapon = WP_DEMP2;
01148
01149 VectorSet( missile->r.maxs, DEMP2_SIZE, DEMP2_SIZE, DEMP2_SIZE );
01150 VectorScale( missile->r.maxs, -1, missile->r.mins );
01151 missile->damage = damage;
01152 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01153 missile->methodOfDeath = MOD_DEMP2;
01154 missile->clipmask = MASK_SHOT;
01155
01156
01157 missile->bounceCount = 0;
01158 }
01159
01160 static gentity_t *ent_list[MAX_GENTITIES];
01161
01162 void DEMP2_AltRadiusDamage( gentity_t *ent )
01163 {
01164 float frac = ( level.time - ent->genericValue5 ) / 800.0f;
01165 float dist, radius, fact;
01166 gentity_t *gent;
01167 int iEntityList[MAX_GENTITIES];
01168 gentity_t *entityList[MAX_GENTITIES];
01169 gentity_t *myOwner = NULL;
01170 int numListedEntities, i, e;
01171 vec3_t mins, maxs;
01172 vec3_t v, dir;
01173
01174 if (ent->r.ownerNum >= 0 &&
01175 ent->r.ownerNum < MAX_GENTITIES)
01176 {
01177 myOwner = &g_entities[ent->r.ownerNum];
01178 }
01179
01180 if (!myOwner || !myOwner->inuse || !myOwner->client)
01181 {
01182 ent->think = G_FreeEntity;
01183 ent->nextthink = level.time;
01184 return;
01185 }
01186
01187 frac *= frac * frac;
01188
01189 radius = frac * 200.0f;
01190
01191 fact = ent->count*0.6;
01192
01193 if (fact < 1)
01194 {
01195 fact = 1;
01196 }
01197
01198 radius *= fact;
01199
01200 for ( i = 0 ; i < 3 ; i++ )
01201 {
01202 mins[i] = ent->r.currentOrigin[i] - radius;
01203 maxs[i] = ent->r.currentOrigin[i] + radius;
01204 }
01205
01206 numListedEntities = trap_EntitiesInBox( mins, maxs, iEntityList, MAX_GENTITIES );
01207
01208 i = 0;
01209 while (i < numListedEntities)
01210 {
01211 entityList[i] = &g_entities[iEntityList[i]];
01212 i++;
01213 }
01214
01215 for ( e = 0 ; e < numListedEntities ; e++ )
01216 {
01217 gent = entityList[ e ];
01218
01219 if ( !gent || !gent->takedamage || !gent->r.contents )
01220 {
01221 continue;
01222 }
01223
01224
01225 for ( i = 0 ; i < 3 ; i++ )
01226 {
01227 if ( ent->r.currentOrigin[i] < gent->r.absmin[i] )
01228 {
01229 v[i] = gent->r.absmin[i] - ent->r.currentOrigin[i];
01230 }
01231 else if ( ent->r.currentOrigin[i] > gent->r.absmax[i] )
01232 {
01233 v[i] = ent->r.currentOrigin[i] - gent->r.absmax[i];
01234 }
01235 else
01236 {
01237 v[i] = 0;
01238 }
01239 }
01240
01241
01242 v[2] *= 0.5f;
01243
01244 dist = VectorLength( v );
01245
01246 if ( dist >= radius )
01247 {
01248
01249 continue;
01250 }
01251
01252 if (dist+(16*ent->count) < ent->genericValue6)
01253 {
01254
01255 continue;
01256 }
01257
01258 VectorCopy( gent->r.currentOrigin, v );
01259 VectorSubtract( v, ent->r.currentOrigin, dir);
01260
01261
01262 dir[2] += 12;
01263
01264 if (gent != myOwner)
01265 {
01266 G_Damage( gent, myOwner, myOwner, dir, ent->r.currentOrigin, ent->damage, DAMAGE_DEATH_KNOCKBACK, ent->splashMethodOfDeath );
01267 if ( gent->takedamage
01268 && gent->client )
01269 {
01270 if ( gent->client->ps.electrifyTime < level.time )
01271 {
01272 if (gent->s.eType == ET_NPC && gent->s.NPC_class == CLASS_VEHICLE &&
01273 gent->m_pVehicle && (gent->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER || gent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER))
01274 {
01275 gent->client->ps.electrifyTime = level.time + Q_irand( 3000, 4000 );
01276 }
01277 else if ( gent->s.NPC_class != CLASS_VEHICLE
01278 || (gent->m_pVehicle && gent->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER) )
01279 {
01280 gent->client->ps.electrifyTime = level.time + Q_irand( 300, 800 );
01281 }
01282 }
01283 if ( gent->client->ps.powerups[PW_CLOAKED] )
01284 {
01285 Jedi_Decloak( gent );
01286 gent->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 );
01287 }
01288 }
01289 }
01290 }
01291
01292
01293 ent->genericValue6 = radius;
01294
01295 if ( frac < 1.0f )
01296 {
01297
01298 ent->nextthink = level.time + 50;
01299 }
01300 else
01301 {
01302 ent->think = G_FreeEntity;
01303 ent->nextthink = level.time;
01304 }
01305 }
01306
01307
01308 void DEMP2_AltDetonate( gentity_t *ent )
01309
01310 {
01311 gentity_t *efEnt;
01312
01313 G_SetOrigin( ent, ent->r.currentOrigin );
01314 if (!ent->pos1[0] && !ent->pos1[1] && !ent->pos1[2])
01315 {
01316 ent->pos1[1] = 1;
01317 }
01318
01319 efEnt = G_PlayEffect( EFFECT_EXPLOSION_DEMP2ALT, ent->r.currentOrigin, ent->pos1 );
01320
01321 if (efEnt)
01322 {
01323 efEnt->s.weapon = ent->count*2;
01324 }
01325
01326 ent->genericValue5 = level.time;
01327 ent->genericValue6 = 0;
01328 ent->nextthink = level.time + 50;
01329 ent->think = DEMP2_AltRadiusDamage;
01330 ent->s.eType = ET_GENERAL;
01331 }
01332
01333
01334 static void WP_DEMP2_AltFire( gentity_t *ent )
01335
01336 {
01337 int damage = DEMP2_ALT_DAMAGE;
01338 int count, origcount;
01339 float fact;
01340 vec3_t start, end;
01341 trace_t tr;
01342 gentity_t *missile;
01343
01344 VectorCopy( muzzle, start );
01345
01346 VectorMA( start, DEMP2_ALT_RANGE, forward, end );
01347
01348 count = ( level.time - ent->client->ps.weaponChargeTime ) / DEMP2_CHARGE_UNIT;
01349
01350 origcount = count;
01351
01352 if ( count < 1 )
01353 {
01354 count = 1;
01355 }
01356 else if ( count > 3 )
01357 {
01358 count = 3;
01359 }
01360
01361 fact = count*0.8;
01362 if (fact < 1)
01363 {
01364 fact = 1;
01365 }
01366 damage *= fact;
01367
01368 if (!origcount)
01369 {
01370 damage = 1;
01371 }
01372
01373 trap_Trace( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT);
01374
01375 missile = G_Spawn();
01376 G_SetOrigin(missile, tr.endpos);
01377
01378
01379
01380 VectorCopy( tr.plane.normal, missile->pos1 );
01381
01382 missile->count = count;
01383
01384 missile->classname = "demp2_alt_proj";
01385 missile->s.weapon = WP_DEMP2;
01386
01387 missile->think = DEMP2_AltDetonate;
01388 missile->nextthink = level.time;
01389
01390 missile->splashDamage = missile->damage = damage;
01391 missile->splashMethodOfDeath = missile->methodOfDeath = MOD_DEMP2;
01392 missile->splashRadius = DEMP2_ALT_SPLASHRADIUS;
01393
01394 missile->r.ownerNum = ent->s.number;
01395
01396 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01397 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
01398
01399
01400 missile->bounceCount = 0;
01401 }
01402
01403
01404 static void WP_FireDEMP2( gentity_t *ent, qboolean altFire )
01405
01406 {
01407 if ( altFire )
01408 {
01409 WP_DEMP2_AltFire( ent );
01410 }
01411 else
01412 {
01413 WP_DEMP2_MainFire( ent );
01414 }
01415 }
01416
01417
01418
01419
01420
01421
01422
01423
01424
01425
01426
01427
01428 static void WP_FlechetteMainFire( gentity_t *ent )
01429
01430 {
01431 vec3_t fwd, angs;
01432 gentity_t *missile;
01433 int i;
01434
01435 for (i = 0; i < FLECHETTE_SHOTS; i++ )
01436 {
01437 vectoangles( forward, angs );
01438
01439 if (i != 0)
01440 {
01441 angs[PITCH] += crandom() * FLECHETTE_SPREAD;
01442 angs[YAW] += crandom() * FLECHETTE_SPREAD;
01443 }
01444
01445 AngleVectors( angs, fwd, NULL, NULL );
01446
01447 missile = CreateMissile( muzzle, fwd, FLECHETTE_VEL, 10000, ent, qfalse);
01448
01449 missile->classname = "flech_proj";
01450 missile->s.weapon = WP_FLECHETTE;
01451
01452 VectorSet( missile->r.maxs, FLECHETTE_SIZE, FLECHETTE_SIZE, FLECHETTE_SIZE );
01453 VectorScale( missile->r.maxs, -1, missile->r.mins );
01454
01455 missile->damage = FLECHETTE_DAMAGE;
01456 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01457 missile->methodOfDeath = MOD_FLECHETTE;
01458 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
01459
01460
01461 missile->bounceCount = Q_irand(5,8);
01462
01463 missile->flags |= FL_BOUNCE_SHRAPNEL;
01464 }
01465 }
01466
01467
01468 void prox_mine_think( gentity_t *ent )
01469
01470 {
01471 int count, i;
01472 qboolean blow = qfalse;
01473
01474
01475 if ( ent->delay > level.time )
01476 {
01477 count = G_RadiusList( ent->r.currentOrigin, FLECHETTE_MINE_RADIUS_CHECK, ent, qtrue, ent_list );
01478
01479 for ( i = 0; i < count; i++ )
01480 {
01481 if ( ent_list[i]->client && ent_list[i]->health > 0 && ent->activator && ent_list[i]->s.number != ent->activator->s.number )
01482 {
01483 blow = qtrue;
01484 break;
01485 }
01486 }
01487 }
01488 else
01489 {
01490
01491 blow = qtrue;
01492 }
01493
01494 if ( blow )
01495 {
01496 ent->think = laserTrapExplode;
01497 ent->nextthink = level.time + 200;
01498 }
01499 else
01500 {
01501
01502 ent->nextthink = level.time + 500;
01503 }
01504 }
01505
01506
01507 static void WP_TraceSetStart( gentity_t *ent, vec3_t start, vec3_t mins, vec3_t maxs )
01508
01509 {
01510
01511 trace_t tr;
01512 vec3_t entMins;
01513 vec3_t entMaxs;
01514
01515 VectorAdd( ent->r.currentOrigin, ent->r.mins, entMins );
01516 VectorAdd( ent->r.currentOrigin, ent->r.maxs, entMaxs );
01517
01518 if ( G_BoxInBounds( start, mins, maxs, entMins, entMaxs ) )
01519 {
01520 return;
01521 }
01522
01523 if ( !ent->client )
01524 {
01525 return;
01526 }
01527
01528 trap_Trace( &tr, ent->client->ps.origin, mins, maxs, start, ent->s.number, MASK_SOLID|CONTENTS_SHOTCLIP );
01529
01530 if ( tr.startsolid || tr.allsolid )
01531 {
01532 return;
01533 }
01534
01535 if ( tr.fraction < 1.0f )
01536 {
01537 VectorCopy( tr.endpos, start );
01538 }
01539 }
01540
01541 void WP_ExplosiveDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
01542 {
01543 laserTrapExplode(self);
01544 }
01545
01546
01547 void WP_flechette_alt_blow( gentity_t *ent )
01548
01549 {
01550 ent->s.pos.trDelta[0] = 1;
01551 ent->s.pos.trDelta[1] = 0;
01552 ent->s.pos.trDelta[2] = 0;
01553
01554 laserTrapExplode(ent);
01555 }
01556
01557
01558 static void WP_CreateFlechetteBouncyThing( vec3_t start, vec3_t fwd, gentity_t *self )
01559
01560 {
01561 gentity_t *missile = CreateMissile( start, fwd, 700 + random() * 700, 1500 + random() * 2000, self, qtrue );
01562
01563 missile->think = WP_flechette_alt_blow;
01564
01565 missile->activator = self;
01566
01567 missile->s.weapon = WP_FLECHETTE;
01568 missile->classname = "flech_alt";
01569 missile->mass = 4;
01570
01571
01572 VectorSet( missile->r.mins, -3.0f, -3.0f, -3.0f );
01573 VectorSet( missile->r.maxs, 3.0f, 3.0f, 3.0f );
01574 missile->clipmask = MASK_SHOT;
01575
01576 missile->touch = touch_NULL;
01577
01578
01579 missile->s.pos.trType = TR_GRAVITY;
01580
01581 missile->flags |= FL_BOUNCE_HALF;
01582 missile->s.eFlags |= EF_ALT_FIRING;
01583
01584 missile->bounceCount = 50;
01585
01586 missile->damage = FLECHETTE_ALT_DAMAGE;
01587 missile->dflags = 0;
01588 missile->splashDamage = FLECHETTE_ALT_SPLASH_DAM;
01589 missile->splashRadius = FLECHETTE_ALT_SPLASH_RAD;
01590
01591 missile->r.svFlags = SVF_USE_CURRENT_ORIGIN;
01592
01593 missile->methodOfDeath = MOD_FLECHETTE_ALT_SPLASH;
01594 missile->splashMethodOfDeath = MOD_FLECHETTE_ALT_SPLASH;
01595
01596 VectorCopy( start, missile->pos2 );
01597 }
01598
01599
01600 static void WP_FlechetteAltFire( gentity_t *self )
01601
01602 {
01603 vec3_t dir, fwd, start, angs;
01604 int i;
01605
01606 vectoangles( forward, angs );
01607 VectorCopy( muzzle, start );
01608
01609 WP_TraceSetStart( self, start, vec3_origin, vec3_origin );
01610
01611 for ( i = 0; i < 2; i++ )
01612 {
01613 VectorCopy( angs, dir );
01614
01615 dir[PITCH] -= random() * 4 + 8;
01616 dir[YAW] += crandom() * 2;
01617 AngleVectors( dir, fwd, NULL, NULL );
01618
01619 WP_CreateFlechetteBouncyThing( start, fwd, self );
01620 }
01621 }
01622
01623
01624 static void WP_FireFlechette( gentity_t *ent, qboolean altFire )
01625
01626 {
01627 if ( altFire )
01628 {
01629
01630 WP_FlechetteAltFire(ent);
01631 }
01632 else
01633 {
01634 WP_FlechetteMainFire( ent );
01635 }
01636 }
01637
01638
01639
01640
01641
01642
01643
01644
01645
01646
01647
01648
01649 void rocketThink( gentity_t *ent )
01650
01651 {
01652 vec3_t newdir, targetdir,
01653 up={0,0,1}, right;
01654 vec3_t org;
01655 float dot, dot2, dis;
01656 int i;
01657 float vel = (ent->spawnflags&1)?ent->speed:ROCKET_VELOCITY;
01658
01659 if ( ent->genericValue1 && ent->genericValue1 < level.time )
01660 {
01661 if ( ent->genericValue2 )
01662 {
01663 RocketDie( ent, &g_entities[ent->r.ownerNum], &g_entities[ent->r.ownerNum], 0, MOD_UNKNOWN );
01664 }
01665 else
01666 {
01667 G_FreeEntity( ent );
01668 }
01669 return;
01670 }
01671 if ( !ent->enemy
01672 || !ent->enemy->client
01673 || ent->enemy->health <= 0
01674 || ent->enemy->client->ps.powerups[PW_CLOAKED] )
01675 {
01676 if ( !ent->genericValue1 )
01677 {
01678 ent->nextthink = level.time + 10000;
01679 ent->think = G_FreeEntity;
01680 }
01681 return;
01682 }
01683
01684 if ( (ent->spawnflags&1) )
01685 {
01686 if ( ent->enemy->client && ent->enemy->client->NPC_class == CLASS_VEHICLE )
01687 {
01688 if ( ent->enemy->client->ps.speed+4000 > vel )
01689 {
01690 vel = ent->enemy->client->ps.speed+4000;
01691 }
01692 }
01693 }
01694
01695 if ( ent->enemy && ent->enemy->inuse )
01696 {
01697 float newDirMult = ent->angle?ent->angle*2.0f:1.0f;
01698 float oldDirMult = ent->angle?(1.0f-ent->angle)*2.0f:1.0f;
01699
01700 VectorCopy( ent->enemy->r.currentOrigin, org );
01701 org[2] += (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5f;
01702
01703 VectorSubtract( org, ent->r.currentOrigin, targetdir );
01704 VectorNormalize( targetdir );
01705
01706
01707 dot = DotProduct( targetdir, ent->movedir );
01708 if ( (ent->spawnflags&1) )
01709 {
01710 if ( ent->radius > -1.0f )
01711 {
01712 if ( dot < ent->radius )
01713 {
01714
01715
01716
01717
01718
01719
01720
01721
01722
01723
01724
01725
01726
01727
01728
01729
01730
01731
01732
01733
01734
01735 return;
01736 }
01737 }
01738 }
01739
01740
01741
01742 if ( dot < 0.0f )
01743 {
01744
01745 CrossProduct( ent->movedir, up, right );
01746 dot2 = DotProduct( targetdir, right );
01747
01748 if ( dot2 > 0 )
01749 {
01750
01751 VectorMA( ent->movedir, 0.4f*newDirMult, right, newdir );
01752 }
01753 else
01754 {
01755
01756 VectorMA( ent->movedir, -0.4f*newDirMult, right, newdir );
01757 }
01758
01759
01760 newdir[2] = ( (targetdir[2]*newDirMult) + (ent->movedir[2]*oldDirMult) ) * 0.5;
01761
01762
01763 vel *= 0.5f;
01764 }
01765 else if ( dot < 0.70f )
01766 {
01767
01768 VectorMA( ent->movedir, 0.5f*newDirMult, targetdir, newdir );
01769 }
01770 else
01771 {
01772
01773 VectorMA( ent->movedir, 0.9f*newDirMult, targetdir, newdir );
01774 }
01775
01776
01777 for (i = 0; i < 3; i++ )
01778 {
01779 newdir[i] += crandom() * ent->random * 0.25f;
01780 }
01781
01782
01783 ent->random *= 0.9f;
01784
01785 if ( ent->enemy->client
01786 && ent->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE )
01787 {
01788
01789 dis = Distance( ent->r.currentOrigin, org );
01790
01791 if ( dis < 128 )
01792 {
01793
01794 newdir[2] -= (1.0f - (dis / 128.0f)) * 0.6f;
01795 }
01796 }
01797
01798 VectorNormalize( newdir );
01799
01800 VectorScale( newdir, vel * 0.5f, ent->s.pos.trDelta );
01801 VectorCopy( newdir, ent->movedir );
01802 SnapVector( ent->s.pos.trDelta );
01803 VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
01804 ent->s.pos.trTime = level.time;
01805 }
01806
01807 ent->nextthink = level.time + ROCKET_ALT_THINK_TIME;
01808 return;
01809 }
01810
01811 extern void G_ExplodeMissile( gentity_t *ent );
01812 void RocketDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
01813 {
01814 self->die = 0;
01815 self->r.contents = 0;
01816
01817 G_ExplodeMissile( self );
01818
01819 self->think = G_FreeEntity;
01820 self->nextthink = level.time;
01821 }
01822
01823
01824 static void WP_FireRocket( gentity_t *ent, qboolean altFire )
01825
01826 {
01827 int damage = ROCKET_DAMAGE;
01828 int vel = ROCKET_VELOCITY;
01829 int dif = 0;
01830 float rTime;
01831 gentity_t *missile;
01832
01833 if ( altFire )
01834 {
01835 vel *= 0.5f;
01836 }
01837
01838 missile = CreateMissile( muzzle, forward, vel, 10000, ent, altFire );
01839
01840 if (ent->client && ent->client->ps.rocketLockIndex != ENTITYNUM_NONE)
01841 {
01842 float lockTimeInterval = ((g_gametype.integer==GT_SIEGE)?2400.0f:1200.0f)/16.0f;
01843 rTime = ent->client->ps.rocketLockTime;
01844
01845 if (rTime == -1)
01846 {
01847 rTime = ent->client->ps.rocketLastValidTime;
01848 }
01849 dif = ( level.time - rTime ) / lockTimeInterval;
01850
01851 if (dif < 0)
01852 {
01853 dif = 0;
01854 }
01855
01856
01857 if ( dif >= 10 && rTime != -1 )
01858 {
01859 missile->enemy = &g_entities[ent->client->ps.rocketLockIndex];
01860
01861 if (missile->enemy && missile->enemy->client && missile->enemy->health > 0 && !OnSameTeam(ent, missile->enemy))
01862 {
01863 missile->angle = 0.5f;
01864 missile->think = rocketThink;
01865 missile->nextthink = level.time + ROCKET_ALT_THINK_TIME;
01866 }
01867 }
01868
01869 ent->client->ps.rocketLockIndex = ENTITYNUM_NONE;
01870 ent->client->ps.rocketLockTime = 0;
01871 ent->client->ps.rocketTargetTime = 0;
01872 }
01873
01874 missile->classname = "rocket_proj";
01875 missile->s.weapon = WP_ROCKET_LAUNCHER;
01876
01877
01878 VectorSet( missile->r.maxs, ROCKET_SIZE, ROCKET_SIZE, ROCKET_SIZE );
01879 VectorScale( missile->r.maxs, -1, missile->r.mins );
01880
01881 missile->damage = damage;
01882 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01883 if (altFire)
01884 {
01885 missile->methodOfDeath = MOD_ROCKET_HOMING;
01886 missile->splashMethodOfDeath = MOD_ROCKET_HOMING_SPLASH;
01887 }
01888 else
01889 {
01890 missile->methodOfDeath = MOD_ROCKET;
01891 missile->splashMethodOfDeath = MOD_ROCKET_SPLASH;
01892 }
01893
01894 missile->health = 10;
01895 missile->takedamage = qtrue;
01896 missile->r.contents = MASK_SHOT;
01897 missile->die = RocketDie;
01898
01899
01900 missile->clipmask = MASK_SHOT;
01901 missile->splashDamage = ROCKET_SPLASH_DAMAGE;
01902 missile->splashRadius = ROCKET_SPLASH_RADIUS;
01903
01904
01905 missile->bounceCount = 0;
01906 }
01907
01908
01909
01910
01911
01912
01913
01914
01915
01916 #define TD_DAMAGE 70 //only do 70 on a direct impact
01917 #define TD_SPLASH_RAD 128
01918 #define TD_SPLASH_DAM 90
01919 #define TD_VELOCITY 900
01920 #define TD_MIN_CHARGE 0.15f
01921 #define TD_TIME 3000//6000
01922 #define TD_ALT_TIME 3000
01923
01924 #define TD_ALT_DAMAGE 60//100
01925 #define TD_ALT_SPLASH_RAD 128
01926 #define TD_ALT_SPLASH_DAM 50//90
01927 #define TD_ALT_VELOCITY 600
01928 #define TD_ALT_MIN_CHARGE 0.15f
01929 #define TD_ALT_TIME 3000
01930
01931 void thermalThinkStandard(gentity_t *ent);
01932
01933
01934 void thermalDetonatorExplode( gentity_t *ent )
01935
01936 {
01937 if ( !ent->count )
01938 {
01939 G_Sound( ent, CHAN_WEAPON, G_SoundIndex( "sound/weapons/thermal/warning.wav" ) );
01940 ent->count = 1;
01941 ent->genericValue5 = level.time + 500;
01942 ent->think = thermalThinkStandard;
01943 ent->nextthink = level.time;
01944 ent->r.svFlags |= SVF_BROADCAST;
01945 }
01946 else
01947 {
01948 vec3_t origin;
01949 vec3_t dir={0,0,1};
01950
01951 BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
01952 origin[2] += 8;
01953 SnapVector( origin );
01954 G_SetOrigin( ent, origin );
01955
01956 ent->s.eType = ET_GENERAL;
01957 G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) );
01958 ent->freeAfterEvent = qtrue;
01959
01960 if (G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius,
01961 ent, ent, ent->splashMethodOfDeath))
01962 {
01963 g_entities[ent->r.ownerNum].client->accuracy_hits++;
01964 }
01965
01966 trap_LinkEntity( ent );
01967 }
01968 }
01969
01970 void thermalThinkStandard(gentity_t *ent)
01971 {
01972 if (ent->genericValue5 < level.time)
01973 {
01974 ent->think = thermalDetonatorExplode;
01975 ent->nextthink = level.time;
01976 return;
01977 }
01978
01979 G_RunObject(ent);
01980 ent->nextthink = level.time;
01981 }
01982
01983
01984 gentity_t *WP_FireThermalDetonator( gentity_t *ent, qboolean altFire )
01985
01986 {
01987 gentity_t *bolt;
01988 vec3_t dir, start;
01989 float chargeAmount = 1.0f;
01990
01991 VectorCopy( forward, dir );
01992 VectorCopy( muzzle, start );
01993
01994 bolt = G_Spawn();
01995
01996 bolt->physicsObject = qtrue;
01997
01998 bolt->classname = "thermal_detonator";
01999 bolt->think = thermalThinkStandard;
02000 bolt->nextthink = level.time;
02001 bolt->touch = touch_NULL;
02002
02003
02004 VectorSet( bolt->r.mins, -3.0f, -3.0f, -3.0f );
02005 VectorSet( bolt->r.maxs, 3.0f, 3.0f, 3.0f );
02006 bolt->clipmask = MASK_SHOT;
02007
02008 W_TraceSetStart( ent, start, bolt->r.mins, bolt->r.maxs );
02009
02010 if ( ent->client )
02011 {
02012 chargeAmount = level.time - ent->client->ps.weaponChargeTime;
02013 }
02014
02015
02016 chargeAmount = chargeAmount / (float)TD_VELOCITY;
02017
02018 if ( chargeAmount > 1.0f )
02019 {
02020 chargeAmount = 1.0f;
02021 }
02022 else if ( chargeAmount < TD_MIN_CHARGE )
02023 {
02024 chargeAmount = TD_MIN_CHARGE;
02025 }
02026
02027
02028 bolt->genericValue5 = level.time + TD_TIME;
02029 bolt->s.pos.trType = TR_GRAVITY;
02030 bolt->parent = ent;
02031 bolt->r.ownerNum = ent->s.number;
02032 VectorScale( dir, TD_VELOCITY * chargeAmount, bolt->s.pos.trDelta );
02033
02034 if ( ent->health >= 0 )
02035 {
02036 bolt->s.pos.trDelta[2] += 120;
02037 }
02038
02039 if ( !altFire )
02040 {
02041 bolt->flags |= FL_BOUNCE_HALF;
02042 }
02043
02044 bolt->s.loopSound = G_SoundIndex( "sound/weapons/thermal/thermloop.wav" );
02045 bolt->s.loopIsSoundset = qfalse;
02046
02047 bolt->damage = TD_DAMAGE;
02048 bolt->dflags = 0;
02049 bolt->splashDamage = TD_SPLASH_DAM;
02050 bolt->splashRadius = TD_SPLASH_RAD;
02051
02052 bolt->s.eType = ET_MISSILE;
02053 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
02054 bolt->s.weapon = WP_THERMAL;
02055
02056 bolt->methodOfDeath = MOD_THERMAL;
02057 bolt->splashMethodOfDeath = MOD_THERMAL_SPLASH;
02058
02059 bolt->s.pos.trTime = level.time;
02060 VectorCopy( start, bolt->s.pos.trBase );
02061
02062 SnapVector( bolt->s.pos.trDelta );
02063 VectorCopy (start, bolt->r.currentOrigin);
02064
02065 VectorCopy( start, bolt->pos2 );
02066
02067 bolt->bounceCount = -5;
02068
02069 return bolt;
02070 }
02071
02072 gentity_t *WP_DropThermal( gentity_t *ent )
02073 {
02074 AngleVectors( ent->client->ps.viewangles, forward, vright, up );
02075 return (WP_FireThermalDetonator( ent, qfalse ));
02076 }
02077
02078
02079
02080 qboolean WP_LobFire( gentity_t *self, vec3_t start, vec3_t target, vec3_t mins, vec3_t maxs, int clipmask,
02081 vec3_t velocity, qboolean tracePath, int ignoreEntNum, int enemyNum,
02082 float minSpeed, float maxSpeed, float idealSpeed, qboolean mustHit )
02083
02084 {
02085 float targetDist, shotSpeed, speedInc = 100, travelTime, impactDist, bestImpactDist = Q3_INFINITE;
02086 vec3_t targetDir, shotVel, failCase;
02087 trace_t trace;
02088 trajectory_t tr;
02089 qboolean blocked;
02090 int elapsedTime, skipNum, timeStep = 500, hitCount = 0, maxHits = 7;
02091 vec3_t lastPos, testPos;
02092 gentity_t *traceEnt;
02093
02094 if ( !idealSpeed )
02095 {
02096 idealSpeed = 300;
02097 }
02098 else if ( idealSpeed < speedInc )
02099 {
02100 idealSpeed = speedInc;
02101 }
02102 shotSpeed = idealSpeed;
02103 skipNum = (idealSpeed-speedInc)/speedInc;
02104 if ( !minSpeed )
02105 {
02106 minSpeed = 100;
02107 }
02108 if ( !maxSpeed )
02109 {
02110 maxSpeed = 900;
02111 }
02112 while ( hitCount < maxHits )
02113 {
02114 VectorSubtract( target, start, targetDir );
02115 targetDist = VectorNormalize( targetDir );
02116
02117 VectorScale( targetDir, shotSpeed, shotVel );
02118 travelTime = targetDist/shotSpeed;
02119 shotVel[2] += travelTime * 0.5 * g_gravity.value;
02120
02121 if ( !hitCount )
02122 {
02123 if ( !mustHit )
02124 {
02125 VectorCopy( shotVel, failCase );
02126 }
02127 }
02128
02129 if ( tracePath )
02130 {
02131 blocked = qfalse;
02132
02133 VectorCopy( start, tr.trBase );
02134 VectorCopy( shotVel, tr.trDelta );
02135 tr.trType = TR_GRAVITY;
02136 tr.trTime = level.time;
02137 travelTime *= 1000.0f;
02138 VectorCopy( start, lastPos );
02139
02140
02141 for ( elapsedTime = timeStep; elapsedTime < floor(travelTime)+timeStep; elapsedTime += timeStep )
02142 {
02143 if ( (float)elapsedTime > travelTime )
02144 {
02145 elapsedTime = floor( travelTime );
02146 }
02147 BG_EvaluateTrajectory( &tr, level.time + elapsedTime, testPos );
02148 trap_Trace( &trace, lastPos, mins, maxs, testPos, ignoreEntNum, clipmask );
02149
02150 if ( trace.allsolid || trace.startsolid )
02151 {
02152 blocked = qtrue;
02153 break;
02154 }
02155 if ( trace.fraction < 1.0f )
02156 {
02157 if ( trace.entityNum == enemyNum )
02158 {
02159 break;
02160 }
02161 else if ( trace.plane.normal[2] > 0.7 && DistanceSquared( trace.endpos, target ) < 4096 )
02162 {
02163 break;
02164 }
02165 else
02166 {
02167 impactDist = DistanceSquared( trace.endpos, target );
02168 if ( impactDist < bestImpactDist )
02169 {
02170 bestImpactDist = impactDist;
02171 VectorCopy( shotVel, failCase );
02172 }
02173 blocked = qtrue;
02174
02175 if ( trace.entityNum < ENTITYNUM_WORLD )
02176 {
02177 traceEnt = &g_entities[trace.entityNum];
02178 if ( traceEnt && traceEnt->takedamage && !OnSameTeam( self, traceEnt ) )
02179 {
02180
02181 VectorCopy( shotVel, failCase );
02182 }
02183 }
02184 break;
02185 }
02186 }
02187 if ( elapsedTime == floor( travelTime ) )
02188 {
02189 break;
02190 }
02191 else
02192 {
02193
02194 VectorCopy( testPos, lastPos );
02195 }
02196 }
02197 if ( blocked )
02198 {
02199 hitCount++;
02200 shotSpeed = idealSpeed + ((hitCount-skipNum) * speedInc);
02201 if ( hitCount >= skipNum )
02202 {
02203 shotSpeed += speedInc;
02204 }
02205 }
02206 else
02207 {
02208 break;
02209 }
02210 }
02211 else
02212 {
02213 break;
02214 }
02215 }
02216
02217 if ( hitCount >= maxHits )
02218 {
02219 VectorCopy( failCase, velocity );
02220 return qfalse;
02221 }
02222 VectorCopy( shotVel, velocity );
02223 return qtrue;
02224 }
02225
02226
02227
02228
02229
02230
02231
02232
02233 #define LT_DAMAGE 100
02234 #define LT_SPLASH_RAD 256.0f
02235 #define LT_SPLASH_DAM 105
02236 #define LT_VELOCITY 900.0f
02237 #define LT_SIZE 1.5f
02238 #define LT_ALT_TIME 2000
02239 #define LT_ACTIVATION_DELAY 1000
02240 #define LT_DELAY_TIME 50
02241
02242 void laserTrapExplode( gentity_t *self )
02243 {
02244 vec3_t v;
02245 self->takedamage = qfalse;
02246
02247 if (self->activator)
02248 {
02249 G_RadiusDamage( self->r.currentOrigin, self->activator, self->splashDamage, self->splashRadius, self, self, MOD_TRIP_MINE_SPLASH );
02250 }
02251
02252 if (self->s.weapon != WP_FLECHETTE)
02253 {
02254 G_AddEvent( self, EV_MISSILE_MISS, 0);
02255 }
02256
02257 VectorCopy(self->s.pos.trDelta, v);
02258
02259
02260 if (self->s.time == -2)
02261 {
02262 v[0] = 0;
02263 v[1] = 0;
02264 v[2] = 0;
02265 }
02266
02267 if (self->s.weapon == WP_FLECHETTE)
02268 {
02269 G_PlayEffect(EFFECT_EXPLOSION_FLECHETTE, self->r.currentOrigin, v);
02270 }
02271 else
02272 {
02273 G_PlayEffect(EFFECT_EXPLOSION_TRIPMINE, self->r.currentOrigin, v);
02274 }
02275
02276 self->think = G_FreeEntity;
02277 self->nextthink = level.time;
02278 }
02279
02280 void laserTrapDelayedExplode( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath )
02281 {
02282 self->enemy = attacker;
02283 self->think = laserTrapExplode;
02284 self->nextthink = level.time + FRAMETIME;
02285 self->takedamage = qfalse;
02286 if ( attacker && !attacker->s.number )
02287 {
02288
02289 self->splashDamage /= 3;
02290 self->splashRadius /= 3;
02291 }
02292 }
02293
02294 void touchLaserTrap( gentity_t *ent, gentity_t *other, trace_t *trace )
02295 {
02296 if (other && other->s.number < ENTITYNUM_WORLD)
02297 {
02298
02299 if ( ent->activator != other )
02300 {
02301 ent->touch = 0;
02302 ent->nextthink = level.time + FRAMETIME;
02303 ent->think = laserTrapExplode;
02304 VectorCopy(trace->plane.normal, ent->s.pos.trDelta);
02305 }
02306 }
02307 else
02308 {
02309 ent->touch = 0;
02310 if (trace->entityNum != ENTITYNUM_NONE)
02311 {
02312 ent->enemy = &g_entities[trace->entityNum];
02313 }
02314 laserTrapStick(ent, trace->endpos, trace->plane.normal);
02315 }
02316 }
02317
02318 void proxMineThink(gentity_t *ent)
02319 {
02320 int i = 0;
02321 gentity_t *cl;
02322 gentity_t *owner = NULL;
02323
02324 if (ent->r.ownerNum < ENTITYNUM_WORLD)
02325 {
02326 owner = &g_entities[ent->r.ownerNum];
02327 }
02328
02329 ent->nextthink = level.time;
02330
02331 if (ent->genericValue15 < level.time ||
02332 !owner ||
02333 !owner->inuse ||
02334 !owner->client ||
02335 owner->client->pers.connected != CON_CONNECTED)
02336 {
02337 ent->think = laserTrapExplode;
02338 return;
02339 }
02340
02341 while (i < MAX_CLIENTS)
02342 {
02343 cl = &g_entities[i];
02344
02345 if (cl->inuse && cl->client && cl->client->pers.connected == CON_CONNECTED &&
02346 owner != cl && cl->client->sess.sessionTeam != TEAM_SPECTATOR &&
02347 cl->client->tempSpectate < level.time && cl->health > 0)
02348 {
02349 if (!OnSameTeam(owner, cl) || g_friendlyFire.integer)
02350 {
02351 vec3_t v;
02352
02353 VectorSubtract(ent->r.currentOrigin, cl->client->ps.origin, v);
02354 if (VectorLength(v) < (ent->splashRadius/2.0f))
02355 {
02356 ent->think = laserTrapExplode;
02357 return;
02358 }
02359 }
02360 }
02361 i++;
02362 }
02363 }
02364
02365 void laserTrapThink ( gentity_t *ent )
02366 {
02367 gentity_t *traceEnt;
02368 vec3_t end;
02369 trace_t tr;
02370
02371
02372 trap_LinkEntity(ent);
02373
02374
02375 if ( !(ent->s.eFlags&EF_FIRING) )
02376 {
02377 G_Sound( ent, CHAN_WEAPON, G_SoundIndex( "sound/weapons/laser_trap/warning.wav" ) );
02378 ent->s.eFlags |= EF_FIRING;
02379 }
02380 ent->think = laserTrapThink;
02381 ent->nextthink = level.time + FRAMETIME;
02382
02383
02384 VectorMA ( ent->s.pos.trBase, 1024, ent->movedir, end );
02385 trap_Trace ( &tr, ent->r.currentOrigin, NULL, NULL, end, ent->s.number, MASK_SHOT);
02386
02387 traceEnt = &g_entities[ tr.entityNum ];
02388
02389 ent->s.time = -1;
02390
02391 if ( traceEnt->client || tr.startsolid )
02392 {
02393
02394 ent->touch = 0;
02395 ent->nextthink = level.time + LT_DELAY_TIME;
02396 ent->think = laserTrapExplode;
02397 }
02398 }
02399
02400 void laserTrapStick( gentity_t *ent, vec3_t endpos, vec3_t normal )
02401 {
02402 G_SetOrigin( ent, endpos );
02403 VectorCopy( normal, ent->pos1 );
02404
02405 VectorClear( ent->s.apos.trDelta );
02406
02407 VectorCopy( normal, ent->s.pos.trDelta );
02408
02409 ent->s.pos.trTime = level.time;
02410
02411
02412
02413 vectoangles( normal, ent->s.apos.trBase );
02414 VectorClear( ent->s.apos.trDelta );
02415 ent->s.apos.trType = TR_STATIONARY;
02416 VectorCopy( ent->s.apos.trBase, ent->s.angles );
02417 VectorCopy( ent->s.angles, ent->r.currentAngles );
02418
02419
02420 G_Sound( ent, CHAN_WEAPON, G_SoundIndex( "sound/weapons/laser_trap/stick.wav" ) );
02421 if ( ent->count )
02422 {
02423
02424 VectorCopy( normal, ent->movedir );
02425 ent->think = laserTrapThink;
02426 ent->nextthink = level.time + LT_ACTIVATION_DELAY;
02427 ent->touch = touch_NULL;
02428
02429 ent->takedamage = qtrue;
02430 ent->health = 5;
02431 ent->die = laserTrapDelayedExplode;
02432
02433
02434 VectorSet( ent->r.mins, -LT_SIZE*2, -LT_SIZE*2, -LT_SIZE*2 );
02435 VectorSet( ent->r.maxs, LT_SIZE*2, LT_SIZE*2, LT_SIZE*2 );
02436
02437
02438 ent->r.svFlags |= SVF_OWNERNOTSHARED;
02439 }
02440 else
02441 {
02442 ent->touch = touchLaserTrap;
02443 ent->think = proxMineThink;
02444 ent->genericValue15 = level.time + 30000;
02445 ent->nextthink = level.time + LT_ALT_TIME;
02446
02447
02448 ent->takedamage = qtrue;
02449 ent->health = 5;
02450 ent->die = laserTrapDelayedExplode;
02451
02452
02453 VectorSet( ent->r.mins, -LT_SIZE*2, -LT_SIZE*2, -LT_SIZE*2 );
02454 VectorSet( ent->r.maxs, LT_SIZE*2, LT_SIZE*2, LT_SIZE*2 );
02455
02456
02457 ent->r.svFlags |= SVF_OWNERNOTSHARED;
02458
02459 if ( !(ent->s.eFlags&EF_FIRING) )
02460 {
02461 G_Sound( ent, CHAN_WEAPON, G_SoundIndex( "sound/weapons/laser_trap/warning.wav" ) );
02462 ent->s.eFlags |= EF_FIRING;
02463 ent->s.time = -1;
02464 ent->s.bolt2 = 1;
02465 }
02466 }
02467 }
02468
02469 void TrapThink(gentity_t *ent)
02470 {
02471 ent->nextthink = level.time + 50;
02472 G_RunObject(ent);
02473 }
02474
02475 void CreateLaserTrap( gentity_t *laserTrap, vec3_t start, gentity_t *owner )
02476 {
02477 laserTrap->classname = "laserTrap";
02478 laserTrap->flags |= FL_BOUNCE_HALF;
02479 laserTrap->s.eFlags |= EF_MISSILE_STICK;
02480 laserTrap->splashDamage = LT_SPLASH_DAM;
02481 laserTrap->splashRadius = LT_SPLASH_RAD;
02482 laserTrap->damage = LT_DAMAGE;
02483 laserTrap->methodOfDeath = MOD_TRIP_MINE_SPLASH;
02484 laserTrap->splashMethodOfDeath = MOD_TRIP_MINE_SPLASH;
02485 laserTrap->s.eType = ET_GENERAL;
02486 laserTrap->r.svFlags = SVF_USE_CURRENT_ORIGIN;
02487 laserTrap->s.weapon = WP_TRIP_MINE;
02488 laserTrap->s.pos.trType = TR_GRAVITY;
02489 laserTrap->r.contents = MASK_SHOT;
02490 laserTrap->parent = owner;
02491 laserTrap->activator = owner;
02492 laserTrap->r.ownerNum = owner->s.number;
02493 VectorSet( laserTrap->r.mins, -LT_SIZE, -LT_SIZE, -LT_SIZE );
02494 VectorSet( laserTrap->r.maxs, LT_SIZE, LT_SIZE, LT_SIZE );
02495 laserTrap->clipmask = MASK_SHOT;
02496 laserTrap->s.solid = 2;
02497 laserTrap->s.modelindex = G_ModelIndex( "models/weapons2/laser_trap/laser_trap_w.glm" );
02498 laserTrap->s.modelGhoul2 = 1;
02499 laserTrap->s.g2radius = 40;
02500
02501 laserTrap->s.genericenemyindex = owner->s.number+MAX_GENTITIES;
02502
02503 laserTrap->health = 1;
02504
02505 laserTrap->s.time = 0;
02506
02507 laserTrap->s.pos.trTime = level.time;
02508 VectorCopy( start, laserTrap->s.pos.trBase );
02509 SnapVector( laserTrap->s.pos.trBase );
02510
02511 SnapVector( laserTrap->s.pos.trDelta );
02512 VectorCopy (start, laserTrap->r.currentOrigin);
02513
02514 laserTrap->s.apos.trType = TR_GRAVITY;
02515 laserTrap->s.apos.trTime = level.time;
02516 laserTrap->s.apos.trBase[YAW] = rand()%360;
02517 laserTrap->s.apos.trBase[PITCH] = rand()%360;
02518 laserTrap->s.apos.trBase[ROLL] = rand()%360;
02519
02520 if (rand()%10 < 5)
02521 {
02522 laserTrap->s.apos.trBase[YAW] = -laserTrap->s.apos.trBase[YAW];
02523 }
02524
02525 VectorCopy( start, laserTrap->pos2 );
02526 laserTrap->touch = touchLaserTrap;
02527 laserTrap->think = TrapThink;
02528 laserTrap->nextthink = level.time + 50;
02529 }
02530
02531 void WP_PlaceLaserTrap( gentity_t *ent, qboolean alt_fire )
02532 {
02533 gentity_t *laserTrap;
02534 gentity_t *found = NULL;
02535 vec3_t dir, start;
02536 int trapcount = 0;
02537 int foundLaserTraps[MAX_GENTITIES];
02538 int trapcount_org;
02539 int lowestTimeStamp;
02540 int removeMe;
02541 int i;
02542
02543 foundLaserTraps[0] = ENTITYNUM_NONE;
02544
02545 VectorCopy( forward, dir );
02546 VectorCopy( muzzle, start );
02547
02548 laserTrap = G_Spawn();
02549
02550
02551
02552 while ( (found = G_Find( found, FOFS(classname), "laserTrap" )) != NULL )
02553 {
02554 if ( found->parent != ent )
02555 {
02556 continue;
02557 }
02558 foundLaserTraps[trapcount++] = found->s.number;
02559 }
02560
02561 found = NULL;
02562 trapcount_org = trapcount;
02563 lowestTimeStamp = level.time;
02564 while ( trapcount > 9 )
02565 {
02566 removeMe = -1;
02567 for ( i = 0; i < trapcount_org; i++ )
02568 {
02569 if ( foundLaserTraps[i] == ENTITYNUM_NONE )
02570 {
02571 continue;
02572 }
02573 found = &g_entities[foundLaserTraps[i]];
02574 if ( laserTrap && found->setTime < lowestTimeStamp )
02575 {
02576 removeMe = i;
02577 lowestTimeStamp = found->setTime;
02578 }
02579 }
02580 if ( removeMe != -1 )
02581 {
02582
02583 if ( &g_entities[foundLaserTraps[removeMe]] == NULL )
02584 {
02585 break;
02586 }
02587 else
02588 {
02589 G_FreeEntity( &g_entities[foundLaserTraps[removeMe]] );
02590 }
02591 foundLaserTraps[removeMe] = ENTITYNUM_NONE;
02592 trapcount--;
02593 }
02594 else
02595 {
02596 break;
02597 }
02598 }
02599
02600
02601 CreateLaserTrap( laserTrap, start, ent );
02602
02603
02604 laserTrap->setTime = level.time;
02605
02606 if (!alt_fire)
02607 {
02608 laserTrap->count = 1;
02609 }
02610
02611
02612 laserTrap->s.pos.trType = TR_GRAVITY;
02613
02614 if (alt_fire)
02615 {
02616 VectorScale( dir, 512, laserTrap->s.pos.trDelta );
02617 }
02618 else
02619 {
02620 VectorScale( dir, 256, laserTrap->s.pos.trDelta );
02621 }
02622
02623 trap_LinkEntity(laserTrap);
02624 }
02625
02626
02627
02628
02629
02630
02631
02632
02633
02634 void VectorNPos(vec3_t in, vec3_t out)
02635 {
02636 if (in[0] < 0) { out[0] = -in[0]; } else { out[0] = in[0]; }
02637 if (in[1] < 0) { out[1] = -in[1]; } else { out[1] = in[1]; }
02638 if (in[2] < 0) { out[2] = -in[2]; } else { out[2] = in[2]; }
02639 }
02640
02641 void DetPackBlow(gentity_t *self);
02642
02643 void charge_stick (gentity_t *self, gentity_t *other, trace_t *trace)
02644 {
02645 gentity_t *tent;
02646
02647 if ( other
02648 && (other->flags&FL_BBRUSH)
02649 && other->s.pos.trType == TR_STATIONARY
02650 && other->s.apos.trType == TR_STATIONARY )
02651 {
02652 self->target_ent = other;
02653 }
02654 else if ( other
02655 && other->s.number < ENTITYNUM_WORLD
02656 && other->s.eType == ET_MOVER
02657 && trace->plane.normal[2] > 0 )
02658 {
02659 self->s.groundEntityNum = other->s.number;
02660 }
02661 else if (other && other->s.number < ENTITYNUM_WORLD &&
02662 (other->client || !other->s.weapon))
02663 {
02664 vec3_t vNor, tN;
02665
02666 VectorCopy(trace->plane.normal, vNor);
02667 VectorNormalize(vNor);
02668 VectorNPos(self->s.pos.trDelta, tN);
02669 self->s.pos.trDelta[0] += vNor[0]*(tN[0]*(((float)Q_irand(1, 10))*0.1));
02670 self->s.pos.trDelta[1] += vNor[1]*(tN[1]*(((float)Q_irand(1, 10))*0.1));
02671 self->s.pos.trDelta[2] += vNor[1]*(tN[2]*(((float)Q_irand(1, 10))*0.1));
02672
02673 vectoangles(vNor, self->s.angles);
02674 vectoangles(vNor, self->s.apos.trBase);
02675 self->touch = charge_stick;
02676 return;
02677 }
02678 else if (other && other->s.number < ENTITYNUM_WORLD)
02679 {
02680 vec3_t v;
02681
02682 self->touch = 0;
02683 self->think = 0;
02684 self->nextthink = 0;
02685
02686 self->takedamage = qfalse;
02687
02688 VectorClear(self->s.apos.trDelta);
02689 self->s.apos.trType = TR_STATIONARY;
02690
02691 G_RadiusDamage( self->r.currentOrigin, self->parent, self->splashDamage, self->splashRadius, self, self, MOD_DET_PACK_SPLASH );
02692 VectorCopy(trace->plane.normal, v);
02693 VectorCopy(v, self->pos2);
02694 self->count = -1;
02695 G_PlayEffect(EFFECT_EXPLOSION_DETPACK, self->r.currentOrigin, v);
02696
02697 self->think = G_FreeEntity;
02698 self->nextthink = level.time;
02699 return;
02700 }
02701
02702
02703
02704 self->touch = 0;
02705 self->think = DetPackBlow;
02706 self->nextthink = level.time + 30000;
02707
02708 VectorClear(self->s.apos.trDelta);
02709 self->s.apos.trType = TR_STATIONARY;
02710
02711 self->s.pos.trType = TR_STATIONARY;
02712 VectorCopy( self->r.currentOrigin, self->s.origin );
02713 VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
02714 VectorClear( self->s.pos.trDelta );
02715
02716 VectorClear( self->s.apos.trDelta );
02717
02718 VectorNormalize(trace->plane.normal);
02719
02720 vectoangles(trace->plane.normal, self->s.angles);
02721 VectorCopy(self->s.angles, self->r.currentAngles );
02722 VectorCopy(self->s.angles, self->s.apos.trBase);
02723
02724 VectorCopy(trace->plane.normal, self->pos2);
02725 self->count = -1;
02726
02727 G_Sound(self, CHAN_WEAPON, G_SoundIndex("sound/weapons/detpack/stick.wav"));
02728
02729 tent = G_TempEntity( self->r.currentOrigin, EV_MISSILE_MISS );
02730 tent->s.weapon = 0;
02731 tent->parent = self;
02732 tent->r.ownerNum = self->s.number;
02733
02734
02735 self->r.svFlags |= SVF_OWNERNOTSHARED;
02736 }
02737
02738 void DetPackBlow(gentity_t *self)
02739 {
02740 vec3_t v;
02741
02742 self->pain = 0;
02743 self->die = 0;
02744 self->takedamage = qfalse;
02745
02746 if ( self->target_ent )
02747 {
02748 G_Damage( self->target_ent, self, &g_entities[self->r.ownerNum], v, self->r.currentOrigin, self->damage, 0, MOD_DET_PACK_SPLASH );
02749 }
02750 G_RadiusDamage( self->r.currentOrigin, self->parent, self->splashDamage, self->splashRadius, self, self, MOD_DET_PACK_SPLASH );
02751 v[0] = 0;
02752 v[1] = 0;
02753 v[2] = 1;
02754
02755 if (self->count == -1)
02756 {
02757 VectorCopy(self->pos2, v);
02758 }
02759
02760 G_PlayEffect(EFFECT_EXPLOSION_DETPACK, self->r.currentOrigin, v);
02761
02762 self->think = G_FreeEntity;
02763 self->nextthink = level.time;
02764 }
02765
02766 void DetPackPain(gentity_t *self, gentity_t *attacker, int damage)
02767 {
02768 self->think = DetPackBlow;
02769 self->nextthink = level.time + Q_irand(50, 100);
02770 self->takedamage = qfalse;
02771 }
02772
02773 void DetPackDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
02774 {
02775 self->think = DetPackBlow;
02776 self->nextthink = level.time + Q_irand(50, 100);
02777 self->takedamage = qfalse;
02778 }
02779
02780 void drop_charge (gentity_t *self, vec3_t start, vec3_t dir)
02781 {
02782 gentity_t *bolt;
02783
02784 VectorNormalize (dir);
02785
02786 bolt = G_Spawn();
02787 bolt->classname = "detpack";
02788 bolt->nextthink = level.time + FRAMETIME;
02789 bolt->think = G_RunObject;
02790 bolt->s.eType = ET_GENERAL;
02791 bolt->s.g2radius = 100;
02792 bolt->s.modelGhoul2 = 1;
02793 bolt->s.modelindex = G_ModelIndex("models/weapons2/detpack/det_pack_proj.glm");
02794
02795 bolt->parent = self;
02796 bolt->r.ownerNum = self->s.number;
02797 bolt->damage = 100;
02798 bolt->splashDamage = 200;
02799 bolt->splashRadius = 200;
02800 bolt->methodOfDeath = MOD_DET_PACK_SPLASH;
02801 bolt->splashMethodOfDeath = MOD_DET_PACK_SPLASH;
02802 bolt->clipmask = MASK_SHOT;
02803 bolt->s.solid = 2;
02804 bolt->r.contents = MASK_SHOT;
02805 bolt->touch = charge_stick;
02806
02807 bolt->physicsObject = qtrue;
02808
02809 bolt->s.genericenemyindex = self->s.number+MAX_GENTITIES;
02810
02811
02812 VectorSet( bolt->r.mins, -2, -2, -2 );
02813 VectorSet( bolt->r.maxs, 2, 2, 2 );
02814
02815 bolt->health = 1;
02816 bolt->takedamage = qtrue;
02817 bolt->pain = DetPackPain;
02818 bolt->die = DetPackDie;
02819
02820 bolt->s.weapon = WP_DET_PACK;
02821
02822 bolt->setTime = level.time;
02823
02824 G_SetOrigin(bolt, start);
02825 bolt->s.pos.trType = TR_GRAVITY;
02826 VectorCopy( start, bolt->s.pos.trBase );
02827 VectorScale(dir, 300, bolt->s.pos.trDelta );
02828 bolt->s.pos.trTime = level.time;
02829
02830 bolt->s.apos.trType = TR_GRAVITY;
02831 bolt->s.apos.trTime = level.time;
02832 bolt->s.apos.trBase[YAW] = rand()%360;
02833 bolt->s.apos.trBase[PITCH] = rand()%360;
02834 bolt->s.apos.trBase[ROLL] = rand()%360;
02835
02836 if (rand()%10 < 5)
02837 {
02838 bolt->s.apos.trBase[YAW] = -bolt->s.apos.trBase[YAW];
02839 }
02840
02841 vectoangles(dir, bolt->s.angles);
02842 VectorCopy(bolt->s.angles, bolt->s.apos.trBase);
02843 VectorSet(bolt->s.apos.trDelta, 300, 0, 0 );
02844 bolt->s.apos.trTime = level.time;
02845
02846 trap_LinkEntity(bolt);
02847 }
02848
02849 void BlowDetpacks(gentity_t *ent)
02850 {
02851 gentity_t *found = NULL;
02852
02853 if ( ent->client->ps.hasDetPackPlanted )
02854 {
02855 while ( (found = G_Find( found, FOFS(classname), "detpack") ) != NULL )
02856 {
02857 if ( found->parent == ent )
02858 {
02859 VectorCopy( found->r.currentOrigin, found->s.origin );
02860 found->think = DetPackBlow;
02861 found->nextthink = level.time + 100 + random() * 200;
02862 G_Sound( found, CHAN_BODY, G_SoundIndex("sound/weapons/detpack/warning.wav") );
02863 }
02864 }
02865 ent->client->ps.hasDetPackPlanted = qfalse;
02866 }
02867 }
02868
02869 qboolean CheatsOn(void)
02870 {
02871 if ( !g_cheats.integer )
02872 {
02873 return qfalse;
02874 }
02875 return qtrue;
02876 }
02877
02878 void WP_DropDetPack( gentity_t *ent, qboolean alt_fire )
02879 {
02880 gentity_t *found = NULL;
02881 int trapcount = 0;
02882 int foundDetPacks[MAX_GENTITIES] = {ENTITYNUM_NONE};
02883 int trapcount_org;
02884 int lowestTimeStamp;
02885 int removeMe;
02886 int i;
02887
02888 if ( !ent || !ent->client )
02889 {
02890 return;
02891 }
02892
02893
02894
02895 while ( (found = G_Find( found, FOFS(classname), "detpack" )) != NULL )
02896 {
02897 if ( found->parent != ent )
02898 {
02899 continue;
02900 }
02901 foundDetPacks[trapcount++] = found->s.number;
02902 }
02903
02904 found = NULL;
02905 trapcount_org = trapcount;
02906 lowestTimeStamp = level.time;
02907 while ( trapcount > 9 )
02908 {
02909 removeMe = -1;
02910 for ( i = 0; i < trapcount_org; i++ )
02911 {
02912 if ( foundDetPacks[i] == ENTITYNUM_NONE )
02913 {
02914 continue;
02915 }
02916 found = &g_entities[foundDetPacks[i]];
02917 if ( found->setTime < lowestTimeStamp )
02918 {
02919 removeMe = i;
02920 lowestTimeStamp = found->setTime;
02921 }
02922 }
02923 if ( removeMe != -1 )
02924 {
02925
02926 if ( &g_entities[foundDetPacks[removeMe]] == NULL )
02927 {
02928 break;
02929 }
02930 else
02931 {
02932 if (!CheatsOn())
02933 {
02934 G_FreeEntity( &g_entities[foundDetPacks[removeMe]] );
02935 }
02936 }
02937 foundDetPacks[removeMe] = ENTITYNUM_NONE;
02938 trapcount--;
02939 }
02940 else
02941 {
02942 break;
02943 }
02944 }
02945
02946 if ( alt_fire )
02947 {
02948 BlowDetpacks(ent);
02949 }
02950 else
02951 {
02952 AngleVectors( ent->client->ps.viewangles, forward, vright, up );
02953
02954 CalcMuzzlePoint( ent, forward, vright, up, muzzle );
02955
02956 VectorNormalize( forward );
02957 VectorMA( muzzle, -4, forward, muzzle );
02958 drop_charge( ent, muzzle, forward );
02959
02960 ent->client->ps.hasDetPackPlanted = qtrue;
02961 }
02962 }
02963
02964 #pragma warning(disable : 4701) //local variable may be used without having been initialized
02965 static void WP_FireConcussionAlt( gentity_t *ent )
02966 {
02967 int damage = CONC_ALT_DAMAGE, skip, traces = DISRUPTOR_ALT_TRACES;
02968 qboolean render_impact = qtrue;
02969 vec3_t start, end;
02970 vec3_t muzzle2, dir;
02971 trace_t tr;
02972 gentity_t *traceEnt, *tent;
02973 float shotRange = 8192.0f;
02974 qboolean hitDodged = qfalse;
02975 vec3_t shot_mins, shot_maxs;
02976 int i;
02977
02978
02979 VectorMA( ent->client->ps.velocity, -200, forward, ent->client->ps.velocity );
02980 ent->client->ps.groundEntityNum = ENTITYNUM_NONE;
02981 if ( (ent->client->ps.pm_flags&PMF_DUCKED) )
02982 {
02983 ent->client->ps.pm_time = 100;
02984 }
02985 else
02986 {
02987 ent->client->ps.pm_time = 250;
02988 }
02989
02990
02991
02992
02993 VectorCopy( muzzle, muzzle2 );
02994
02995 VectorCopy( muzzle, start );
02996 WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );
02997
02998 skip = ent->s.number;
02999
03000
03001
03002
03003
03004
03005
03006
03007 VectorSet( shot_mins, -1, -1, -1 );
03008 VectorSet( shot_maxs, 1, 1, 1 );
03009
03010 for ( i = 0; i < traces; i++ )
03011 {
03012 VectorMA( start, shotRange, forward, end );
03013
03014
03015
03016
03017 if (d_projectileGhoul2Collision.integer)
03018 {
03019 trap_G2Trace( &tr, start, shot_mins, shot_maxs, end, skip, MASK_SHOT, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer );
03020 }
03021 else
03022 {
03023 trap_Trace( &tr, start, shot_mins, shot_maxs, end, skip, MASK_SHOT );
03024 }
03025
03026 traceEnt = &g_entities[tr.entityNum];
03027
03028 if (d_projectileGhoul2Collision.integer && traceEnt->inuse && traceEnt->client)
03029 {
03030 if (traceEnt->inuse && traceEnt->client && traceEnt->ghoul2)
03031 {
03032 traceEnt->client->g2LastSurfaceHit = tr.surfaceFlags;
03033 traceEnt->client->g2LastSurfaceTime = level.time;
03034 }
03035
03036 if (traceEnt->ghoul2)
03037 {
03038 tr.surfaceFlags = 0;
03039 }
03040 }
03041 if ( tr.surfaceFlags & SURF_NOIMPACT )
03042 {
03043 render_impact = qfalse;
03044 }
03045
03046 if ( tr.entityNum == ent->s.number )
03047 {
03048
03049
03050 VectorCopy( tr.endpos, muzzle2 );
03051 VectorCopy( tr.endpos, start );
03052 skip = tr.entityNum;
03053 #ifdef _DEBUG
03054 Com_Printf( "BAD! Concussion gun shot somehow traced back and hit the owner!\n" );
03055 #endif
03056 continue;
03057 }
03058
03059
03060
03061
03062
03063
03064
03065
03066 if ( tr.fraction >= 1.0f )
03067 {
03068
03069 break;
03070 }
03071
03072 if ( traceEnt->s.weapon == WP_SABER )
03073 {
03074 hitDodged = Jedi_DodgeEvasion( traceEnt, ent, &tr, HL_NONE );
03075
03076 }
03077 if ( !hitDodged )
03078 {
03079 if ( render_impact )
03080 {
03081 if (( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage )
03082 || !Q_stricmp( traceEnt->classname, "misc_model_breakable" )
03083 || traceEnt->s.eType == ET_MOVER )
03084 {
03085 qboolean noKnockBack;
03086
03087
03088
03089
03090
03091 if ( traceEnt->client && LogAccuracyHit( traceEnt, ent ))
03092 {
03093 ent->client->accuracy_hits++;
03094 }
03095
03096 noKnockBack = (traceEnt->flags&FL_NO_KNOCKBACK);
03097 if ( traceEnt && traceEnt->client && traceEnt->client->NPC_class == CLASS_GALAKMECH )
03098 {
03099 G_Damage( traceEnt, ent, ent, forward, tr.endpos, 10, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_CONC_ALT );
03100 break;
03101 }
03102 G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_CONC_ALT );
03103
03104
03105 if ( traceEnt->client )
03106 {
03107 vec3_t pushDir;
03108 VectorCopy( forward, pushDir );
03109 if ( pushDir[2] < 0.2f )
03110 {
03111 pushDir[2] = 0.2f;
03112 }
03113
03114
03115
03116
03117
03118
03119 if ( traceEnt->health > 0 )
03120 {
03121
03122 if (!noKnockBack && !traceEnt->localAnimIndex && traceEnt->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN &&
03123 BG_KnockDownable(&traceEnt->client->ps))
03124 {
03125
03126 vec3_t plPDif;
03127 float pStr;
03128
03129
03130
03131 VectorSubtract(traceEnt->client->ps.origin, ent->client->ps.origin, plPDif);
03132 pStr = 500.0f-VectorLength(plPDif);
03133 if (pStr < 150.0f)
03134 {
03135 pStr = 150.0f;
03136 }
03137 if (pStr > 200.0f)
03138 {
03139 traceEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
03140 traceEnt->client->ps.forceHandExtendTime = level.time + 1100;
03141 traceEnt->client->ps.forceDodgeAnim = 0;
03142 }
03143 traceEnt->client->ps.otherKiller = ent->s.number;
03144 traceEnt->client->ps.otherKillerTime = level.time + 5000;
03145 traceEnt->client->ps.otherKillerDebounceTime = level.time + 100;
03146
03147 traceEnt->client->ps.velocity[0] += pushDir[0]*pStr;
03148 traceEnt->client->ps.velocity[1] += pushDir[1]*pStr;
03149 traceEnt->client->ps.velocity[2] = pStr;
03150 }
03151 }
03152 }
03153
03154 if ( traceEnt->s.eType == ET_MOVER )
03155 {
03156 break;
03157 }
03158 }
03159 else
03160 {
03161
03162
03163
03164
03165
03166
03167
03168
03169
03170
03171 break;
03172 }
03173 }
03174 else
03175 {
03176 break;
03177 }
03178 }
03179
03180 VectorCopy( tr.endpos, muzzle2 );
03181 VectorCopy( tr.endpos, start );
03182 skip = tr.entityNum;
03183 hitDodged = qfalse;
03184 }
03185
03186
03187
03188
03189
03190
03191
03192
03193
03194
03195
03196 VectorSubtract( tr.endpos, muzzle, dir );
03197
03198
03199
03200
03201 tent = G_TempEntity(tr.endpos, EV_CONC_ALT_IMPACT);
03202 tent->s.eventParm = DirToByte(tr.plane.normal);
03203 tent->s.owner = ent->s.number;
03204 VectorCopy(dir, tent->s.angles);
03205 VectorCopy(muzzle, tent->s.origin2);
03206 VectorCopy(forward, tent->s.angles2);
03207
03208 #if 0 //yuck
03209
03210 for ( dist = 0; dist < shotDist; dist += 64 )
03211 {
03212
03213 VectorMA( muzzle, dist, dir, spot );
03214 AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
03215
03216 G_PlayEffectID( G_EffectIndex( "concussion/alt_ring" ), spot, actualAngles );
03217 }
03218
03219 VectorMA( start, shotDist-4, forward, spot );
03220 AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
03221
03222 G_PlayEffectID( G_EffectIndex( "concussion/altmuzzle_flash" ), muzzle, forward );
03223 #endif
03224 }
03225 #pragma warning(default : 4701) //local variable may be used without having been initialized
03226
03227 static void WP_FireConcussion( gentity_t *ent )
03228 {
03229 vec3_t start;
03230 int damage = CONC_DAMAGE;
03231 float vel = CONC_VELOCITY;
03232 gentity_t *missile;
03233
03234
03235
03236
03237
03238
03239
03240
03241
03242
03243
03244
03245
03246 VectorCopy( muzzle, start );
03247 WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );
03248
03249 missile = CreateMissile( start, forward, vel, 10000, ent, qfalse );
03250
03251 missile->classname = "conc_proj";
03252 missile->s.weapon = WP_CONCUSSION;
03253 missile->mass = 10;
03254
03255
03256 VectorSet( missile->r.maxs, ROCKET_SIZE, ROCKET_SIZE, ROCKET_SIZE );
03257 VectorScale( missile->r.maxs, -1, missile->r.mins );
03258
03259 missile->damage = damage;
03260 missile->dflags = DAMAGE_EXTRA_KNOCKBACK;
03261
03262 missile->methodOfDeath = MOD_CONC;
03263 missile->splashMethodOfDeath = MOD_CONC;
03264
03265 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
03266 missile->splashDamage = CONC_SPLASH_DAMAGE;
03267 missile->splashRadius = CONC_SPLASH_RADIUS;
03268
03269
03270 missile->bounceCount = 0;
03271 }
03272
03273
03274
03275
03276
03277 void WP_FireStunBaton( gentity_t *ent, qboolean alt_fire )
03278 {
03279 gentity_t *tr_ent;
03280 trace_t tr;
03281 vec3_t mins, maxs, end;
03282 vec3_t muzzleStun;
03283
03284 if (!ent->client)
03285 {
03286 VectorCopy(ent->r.currentOrigin, muzzleStun);
03287 muzzleStun[2] += 8;
03288 }
03289 else
03290 {
03291 VectorCopy(ent->client->ps.origin, muzzleStun);
03292 muzzleStun[2] += ent->client->ps.viewheight-6;
03293 }
03294
03295 VectorMA(muzzleStun, 20.0f, forward, muzzleStun);
03296 VectorMA(muzzleStun, 4.0f, vright, muzzleStun);
03297
03298 VectorMA( muzzleStun, STUN_BATON_RANGE, forward, end );
03299
03300 VectorSet( maxs, 6, 6, 6 );
03301 VectorScale( maxs, -1, mins );
03302
03303 trap_Trace ( &tr, muzzleStun, mins, maxs, end, ent->s.number, MASK_SHOT );
03304
03305 if ( tr.entityNum >= ENTITYNUM_WORLD )
03306 {
03307 return;
03308 }
03309
03310 tr_ent = &g_entities[tr.entityNum];
03311
03312 if (tr_ent && tr_ent->takedamage && tr_ent->client)
03313 {
03314 if (tr_ent->client->ps.duelInProgress &&
03315 tr_ent->client->ps.duelIndex != ent->s.number)
03316 {
03317 return;
03318 }
03319
03320 if (ent->client &&
03321 ent->client->ps.duelInProgress &&
03322 ent->client->ps.duelIndex != tr_ent->s.number)
03323 {
03324 return;
03325 }
03326 }
03327
03328 if ( tr_ent && tr_ent->takedamage )
03329 {
03330 G_PlayEffect( EFFECT_STUNHIT, tr.endpos, tr.plane.normal );
03331
03332 G_Sound( tr_ent, CHAN_WEAPON, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
03333 G_Damage( tr_ent, ent, ent, forward, tr.endpos, STUN_BATON_DAMAGE, (DAMAGE_NO_KNOCKBACK|DAMAGE_HALF_ABSORB), MOD_STUN_BATON );
03334
03335 if (tr_ent->client)
03336 {
03337 if ( tr_ent->client->NPC_class == CLASS_VEHICLE )
03338 {
03339 if ( !tr_ent->m_pVehicle
03340 || tr_ent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL
03341 || tr_ent->m_pVehicle->m_pVehicleInfo->type == VH_FLIER )
03342 {
03343 tr_ent->client->ps.electrifyTime = level.time + Q_irand( 3000, 4000 );
03344 }
03345 }
03346 else
03347 {
03348 tr_ent->client->ps.electrifyTime = level.time + 700;
03349 }
03350 }
03351 }
03352 }
03353
03354
03355
03356
03357
03358 void WP_FireMelee( gentity_t *ent, qboolean alt_fire )
03359 {
03360 gentity_t *tr_ent;
03361 trace_t tr;
03362 vec3_t mins, maxs, end;
03363 vec3_t muzzlePunch;
03364
03365 if (ent->client && ent->client->ps.torsoAnim == BOTH_MELEE2)
03366 {
03367 if (ent->client->ps.brokenLimbs & (1 << BROKENLIMB_RARM))
03368 {
03369 return;
03370 }
03371 }
03372 else
03373 {
03374 if (ent->client->ps.brokenLimbs & (1 << BROKENLIMB_LARM))
03375 {
03376 return;
03377 }
03378 }
03379
03380 if (!ent->client)
03381 {
03382 VectorCopy(ent->r.currentOrigin, muzzlePunch);
03383 muzzlePunch[2] += 8;
03384 }
03385 else
03386 {
03387 VectorCopy(ent->client->ps.origin, muzzlePunch);
03388 muzzlePunch[2] += ent->client->ps.viewheight-6;
03389 }
03390
03391 VectorMA(muzzlePunch, 20.0f, forward, muzzlePunch);
03392 VectorMA(muzzlePunch, 4.0f, vright, muzzlePunch);
03393
03394 VectorMA( muzzlePunch, MELEE_RANGE, forward, end );
03395
03396 VectorSet( maxs, 6, 6, 6 );
03397 VectorScale( maxs, -1, mins );
03398
03399 trap_Trace ( &tr, muzzlePunch, mins, maxs, end, ent->s.number, MASK_SHOT );
03400
03401 if (tr.entityNum != ENTITYNUM_NONE)
03402 {
03403 tr_ent = &g_entities[tr.entityNum];
03404
03405 G_Sound( ent, CHAN_AUTO, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
03406
03407 if (tr_ent->takedamage && tr_ent->client)
03408 {
03409 if (tr_ent->client->ps.duelInProgress &&
03410 tr_ent->client->ps.duelIndex != ent->s.number)
03411 {
03412 return;
03413 }
03414
03415 if (ent->client &&
03416 ent->client->ps.duelInProgress &&
03417 ent->client->ps.duelIndex != tr_ent->s.number)
03418 {
03419 return;
03420 }
03421 }
03422
03423 if ( tr_ent->takedamage )
03424 {
03425 int dmg = MELEE_SWING1_DAMAGE;
03426
03427 if (ent->client && ent->client->ps.torsoAnim == BOTH_MELEE2)
03428 {
03429 dmg = MELEE_SWING2_DAMAGE;
03430 }
03431
03432 if ( G_HeavyMelee( ent ) )
03433 {
03434 dmg *= 2;
03435 }
03436
03437 G_Damage( tr_ent, ent, ent, forward, tr.endpos, dmg, DAMAGE_NO_ARMOR, MOD_MELEE );
03438 }
03439 }
03440 }
03441
03447
03448
03449
03450
03451
03452
03453
03454
03455
03456
03457
03458
03459 void SnapVectorTowards( vec3_t v, vec3_t to ) {
03460 int i;
03461
03462 for ( i = 0 ; i < 3 ; i++ ) {
03463 if ( to[i] <= v[i] ) {
03464 v[i] = (int)v[i];
03465 } else {
03466 v[i] = (int)v[i] + 1;
03467 }
03468 }
03469 }
03470
03471
03472
03473
03474
03475
03476
03477
03478
03479
03480 qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
03481 if( !target->takedamage ) {
03482 return qfalse;
03483 }
03484
03485 if ( target == attacker ) {
03486 return qfalse;
03487 }
03488
03489 if( !target->client ) {
03490 return qfalse;
03491 }
03492
03493 if (!attacker)
03494 {
03495 return qfalse;
03496 }
03497
03498 if( !attacker->client ) {
03499 return qfalse;
03500 }
03501
03502 if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
03503 return qfalse;
03504 }
03505
03506 if ( OnSameTeam( target, attacker ) ) {
03507 return qfalse;
03508 }
03509
03510 return qtrue;
03511 }
03512
03513
03514
03515
03516
03517
03518
03519
03520
03521
03522
03523
03524
03525 void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint )
03526 {
03527 int weapontype;
03528 vec3_t muzzleOffPoint;
03529
03530 weapontype = ent->s.weapon;
03531 VectorCopy( ent->s.pos.trBase, muzzlePoint );
03532
03533 VectorCopy(WP_MuzzlePoint[weapontype], muzzleOffPoint);
03534
03535 if (weapontype > WP_NONE && weapontype < WP_NUM_WEAPONS)
03536 {
03537 {
03538 VectorMA(muzzlePoint, muzzleOffPoint[0], forward, muzzlePoint);
03539 VectorMA(muzzlePoint, muzzleOffPoint[1], right, muzzlePoint);
03540 muzzlePoint[2] += ent->client->ps.viewheight + muzzleOffPoint[2];
03541 }
03542 }
03543
03544
03545 SnapVector( muzzlePoint );
03546 }
03547
03548
03549
03550
03551
03552
03553
03554
03555 void CalcMuzzlePointOrigin ( gentity_t *ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
03556 VectorCopy( ent->s.pos.trBase, muzzlePoint );
03557 muzzlePoint[2] += ent->client->ps.viewheight;
03558 VectorMA( muzzlePoint, 14, forward, muzzlePoint );
03559
03560 SnapVector( muzzlePoint );
03561 }
03562
03563 extern void G_MissileImpact( gentity_t *ent, trace_t *trace );
03564 void WP_TouchVehMissile( gentity_t *ent, gentity_t *other, trace_t *trace )
03565 {
03566 trace_t myTrace;
03567 memcpy( (void *)&myTrace, (void *)trace, sizeof(myTrace) );
03568 if ( other )
03569 {
03570 myTrace.entityNum = other->s.number;
03571 }
03572 G_MissileImpact( ent, &myTrace );
03573 }
03574
03575 void WP_CalcVehMuzzle(gentity_t *ent, int muzzleNum)
03576 {
03577 Vehicle_t *pVeh = ent->m_pVehicle;
03578 mdxaBone_t boltMatrix;
03579 vec3_t vehAngles;
03580
03581 assert(pVeh);
03582
03583 if (pVeh->m_iMuzzleTime[muzzleNum] == level.time)
03584 {
03585 return;
03586 }
03587
03588 pVeh->m_iMuzzleTime[muzzleNum] = level.time;
03589
03590 VectorCopy( ent->client->ps.viewangles, vehAngles );
03591 if ( pVeh->m_pVehicleInfo
03592 && (pVeh->m_pVehicleInfo->type == VH_ANIMAL
03593 ||pVeh->m_pVehicleInfo->type == VH_WALKER
03594 ||pVeh->m_pVehicleInfo->type == VH_SPEEDER) )
03595 {
03596 vehAngles[PITCH] = vehAngles[ROLL] = 0;
03597 }
03598
03599 trap_G2API_GetBoltMatrix_NoRecNoRot(ent->ghoul2, 0, pVeh->m_iMuzzleTag[muzzleNum], &boltMatrix, vehAngles,
03600 ent->client->ps.origin, level.time, NULL, ent->modelScale);
03601 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, pVeh->m_vMuzzlePos[muzzleNum]);
03602 BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, pVeh->m_vMuzzleDir[muzzleNum]);
03603 }
03604
03605 void WP_VehWeapSetSolidToOwner( gentity_t *self )
03606 {
03607 self->r.svFlags |= SVF_OWNERNOTSHARED;
03608 if ( self->genericValue1 )
03609 {
03610 if ( self->genericValue2 )
03611 {
03612 self->think = G_ExplodeMissile;
03613 }
03614 else
03615 {
03616 self->think = G_FreeEntity;
03617 }
03618 self->nextthink = level.time + self->genericValue1;
03619 }
03620 }
03621
03622 #define VEH_HOMING_MISSILE_THINK_TIME 100
03623 gentity_t *WP_FireVehicleWeapon( gentity_t *ent, vec3_t start, vec3_t dir, vehWeaponInfo_t *vehWeapon, qboolean alt_fire, qboolean isTurretWeap )
03624 {
03625 gentity_t *missile = NULL;
03626
03627
03628 if ( !vehWeapon )
03629 {
03630 return NULL;
03631 }
03632 else if ( vehWeapon->bIsProjectile )
03633 {
03634 vec3_t mins, maxs;
03635
03636 VectorSet( maxs, vehWeapon->fWidth/2.0f,vehWeapon->fWidth/2.0f,vehWeapon->fHeight/2.0f );
03637 VectorScale( maxs, -1, mins );
03638
03639
03640 WP_TraceSetStart( ent, start, mins, maxs );
03641
03642
03643
03644 missile = CreateMissile( start, dir, vehWeapon->fSpeed, 10000, ent, qfalse );
03645
03646 missile->classname = "vehicle_proj";
03647
03648 missile->s.genericenemyindex = ent->s.number+MAX_GENTITIES;
03649 missile->damage = vehWeapon->iDamage;
03650 missile->splashDamage = vehWeapon->iSplashDamage;
03651 missile->splashRadius = vehWeapon->fSplashRadius;
03652
03653
03654 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
03655 missile->clipmask = MASK_SHOT;
03656
03657 if ( vehWeapon->bSaberBlockable )
03658 {
03659 missile->clipmask |= CONTENTS_LIGHTSABER;
03660 }
03661
03662
03663
03664
03665
03666
03667
03668
03669
03670
03671
03672 VectorCopy( mins, missile->r.mins );
03673 VectorCopy( maxs, missile->r.maxs );
03674
03675 if ( vehWeapon->fWidth || vehWeapon->fHeight )
03676 {
03677 missile->s.weapon = WP_ROCKET_LAUNCHER;
03678 missile->methodOfDeath = MOD_VEHICLE;
03679 missile->splashMethodOfDeath = MOD_VEHICLE;
03680
03681
03682 missile->bounceCount = 0;
03683
03684 missile->mass = 10;
03685 }
03686 else
03687 {
03688 missile->s.weapon = WP_BLASTER;
03689 missile->methodOfDeath = MOD_VEHICLE;
03690 missile->splashMethodOfDeath = MOD_VEHICLE;
03691
03692 missile->bounceCount = 8;
03693 }
03694
03695 if ( vehWeapon->bHasGravity )
03696 {
03697 missile->s.weapon = WP_THERMAL;
03698 missile->s.pos.trType = TR_GRAVITY;
03699 }
03700
03701 if ( vehWeapon->bIonWeapon )
03702 {
03703 missile->s.weapon = WP_DEMP2;
03704 }
03705
03706 if ( vehWeapon->iHealth )
03707 {
03708 missile->health = vehWeapon->iHealth;
03709 missile->takedamage = qtrue;
03710 missile->r.contents = MASK_SHOT;
03711 missile->die = RocketDie;
03712 }
03713
03714
03715 if (ent->m_pVehicle && ent->m_pVehicle->m_pPilot)
03716 {
03717 missile->r.ownerNum = ent->m_pVehicle->m_pPilot->s.number;
03718 }
03719 else
03720 {
03721 missile->r.ownerNum = ent->s.number;
03722 }
03723
03724
03725 missile->s.owner = ent->s.number;
03726 if ( alt_fire )
03727 {
03728 missile->s.eFlags |= EF_ALT_FIRING;
03729 }
03730 if ( isTurretWeap )
03731 {
03732 missile->s.weapon = WP_TURRET;
03733 }
03734 if ( vehWeapon->iLifeTime )
03735 {
03736 if ( vehWeapon->bExplodeOnExpire )
03737 {
03738 missile->think = G_ExplodeMissile;
03739 }
03740 else
03741 {
03742 missile->think = G_FreeEntity;
03743 }
03744 missile->nextthink = level.time + vehWeapon->iLifeTime;
03745 }
03746 missile->s.otherEntityNum2 = (vehWeapon-&g_vehWeaponInfo[0]);
03747 missile->s.eFlags |= EF_JETPACK_ACTIVE;
03748
03749 if ( vehWeapon->fHoming )
03750 {
03751 if ( ent->client && ent->client->ps.rocketLockIndex != ENTITYNUM_NONE )
03752 {
03753 int dif = 0;
03754 float rTime;
03755 rTime = ent->client->ps.rocketLockTime;
03756
03757 if (rTime == -1)
03758 {
03759 rTime = ent->client->ps.rocketLastValidTime;
03760 }
03761
03762 if ( !vehWeapon->iLockOnTime )
03763 {
03764 dif = 10;
03765 }
03766 else
03767 {
03768 float lockTimeInterval = vehWeapon->iLockOnTime/16.0f;
03769 dif = ( level.time - rTime ) / lockTimeInterval;
03770 }
03771
03772 if (dif < 0)
03773 {
03774 dif = 0;
03775 }
03776
03777
03778 if ( dif >= 10 && rTime != -1 )
03779 {
03780 missile->enemy = &g_entities[ent->client->ps.rocketLockIndex];
03781
03782 if (missile->enemy && missile->enemy->client && missile->enemy->health > 0 && !OnSameTeam(ent, missile->enemy))
03783 {
03784 missile->spawnflags |= 1;
03785 missile->speed = vehWeapon->fSpeed;
03786 missile->angle = vehWeapon->fHoming;
03787 missile->radius = vehWeapon->fHomingFOV;
03788
03789 if ( vehWeapon->iLifeTime )
03790 {
03791 missile->genericValue1 = level.time + vehWeapon->iLifeTime;
03792 missile->genericValue2 = (int)(vehWeapon->bExplodeOnExpire);
03793 }
03794
03795 missile->think = rocketThink;
03796 missile->nextthink = level.time + VEH_HOMING_MISSILE_THINK_TIME;
03797 missile->s.eFlags |= EF_RADAROBJECT;
03798 if ( missile->enemy->s.NPC_class == CLASS_VEHICLE )
03799 {
03800 missile->s.otherEntityNum = missile->enemy->s.number;
03801 }
03802 }
03803 }
03804
03805 VectorCopy( dir, missile->movedir );
03806 missile->random = 1.0f;
03807 }
03808 }
03809 if ( !vehWeapon->fSpeed )
03810 {
03811
03812 missile->s.weapon = WP_THERMAL;
03813 G_SetOrigin( missile, start );
03814 missile->touch = WP_TouchVehMissile;
03815 missile->s.eFlags |= EF_RADAROBJECT;
03816
03817 if ( vehWeapon->iLifeTime )
03818 {
03819 missile->genericValue1 = vehWeapon->iLifeTime;
03820 missile->genericValue2 = (int)(vehWeapon->bExplodeOnExpire);
03821 }
03822
03823 missile->think = WP_VehWeapSetSolidToOwner;
03824 missile->nextthink = level.time + 3000;
03825 }
03826 }
03827 else
03828 {
03829
03830 }
03831
03832 return missile;
03833 }
03834
03835
03836 void G_VehMuzzleFireFX( gentity_t *ent, gentity_t *broadcaster, int muzzlesFired )
03837 {
03838 Vehicle_t *pVeh = ent->m_pVehicle;
03839 gentity_t *b;
03840
03841 if (!pVeh)
03842 {
03843 return;
03844 }
03845
03846 if (!broadcaster)
03847 {
03848 b = G_TempEntity( ent->client->ps.origin, EV_VEH_FIRE );
03849 }
03850 else
03851 {
03852 b = broadcaster;
03853 }
03854
03855
03856 b->s.owner = ent->s.number;
03857
03858
03859
03860 b->s.trickedentindex = muzzlesFired;
03861
03862 if ( broadcaster )
03863 {
03864 G_AddEvent( b, EV_VEH_FIRE, 0 );
03865 }
03866 }
03867
03868 void G_EstimateCamPos( vec3_t viewAngles, vec3_t cameraFocusLoc, float viewheight, float thirdPersonRange,
03869 float thirdPersonHorzOffset, float vertOffset, float pitchOffset,
03870 int ignoreEntNum, vec3_t camPos )
03871 {
03872 int MASK_CAMERACLIP = (MASK_SOLID|CONTENTS_PLAYERCLIP);
03873 float CAMERA_SIZE = 4;
03874 vec3_t cameramins;
03875 vec3_t cameramaxs;
03876 vec3_t cameraFocusAngles, camerafwd, cameraup;
03877 vec3_t cameraIdealTarget, cameraCurTarget;
03878 vec3_t cameraIdealLoc, cameraCurLoc;
03879 vec3_t diff;
03880 vec3_t camAngles;
03881 vec3_t viewaxis[3];
03882 trace_t trace;
03883
03884 VectorSet( cameramins, -CAMERA_SIZE, -CAMERA_SIZE, -CAMERA_SIZE );
03885 VectorSet( cameramaxs, CAMERA_SIZE, CAMERA_SIZE, CAMERA_SIZE );
03886
03887 VectorCopy( viewAngles, cameraFocusAngles );
03888 cameraFocusAngles[PITCH] += pitchOffset;
03889 if ( !bg_fighterAltControl.integer )
03890 {
03891 cameraFocusAngles[PITCH] = AngleNormalize180( cameraFocusAngles[PITCH] );
03892 if (cameraFocusAngles[PITCH] > 80.0)
03893 {
03894 cameraFocusAngles[PITCH] = 80.0;
03895 }
03896 else if (cameraFocusAngles[PITCH] < -80.0)
03897 {
03898 cameraFocusAngles[PITCH] = -80.0;
03899 }
03900 }
03901 AngleVectors(cameraFocusAngles, camerafwd, NULL, cameraup);
03902
03903 cameraFocusLoc[2] += viewheight;
03904
03905 VectorCopy( cameraFocusLoc, cameraIdealTarget );
03906 cameraIdealTarget[2] += vertOffset;
03907
03908
03909 VectorCopy( cameraIdealTarget, cameraCurTarget );
03910 trap_Trace( &trace, cameraFocusLoc, cameramins, cameramaxs, cameraCurTarget, ignoreEntNum, MASK_CAMERACLIP );
03911 if (trace.fraction < 1.0)
03912 {
03913 VectorCopy(trace.endpos, cameraCurTarget);
03914 }
03915
03916 VectorMA(cameraIdealTarget, -(thirdPersonRange), camerafwd, cameraIdealLoc);
03917
03918 VectorCopy( cameraIdealLoc, cameraCurLoc );
03919 trap_Trace(&trace, cameraCurTarget, cameramins, cameramaxs, cameraCurLoc, ignoreEntNum, MASK_CAMERACLIP);
03920 if (trace.fraction < 1.0)
03921 {
03922 VectorCopy( trace.endpos, cameraCurLoc );
03923 }
03924
03925 VectorSubtract(cameraCurTarget, cameraCurLoc, diff);
03926 {
03927 float dist = VectorNormalize(diff);
03928
03929 if ( !dist || (diff[0] == 0 || diff[1] == 0) )
03930 {
03931 VectorCopy( camerafwd, diff );
03932 }
03933 }
03934
03935 vectoangles(diff, camAngles);
03936
03937 if ( thirdPersonHorzOffset != 0.0f )
03938 {
03939 AnglesToAxis( camAngles, viewaxis );
03940 VectorMA( cameraCurLoc, thirdPersonHorzOffset, viewaxis[1], cameraCurLoc );
03941 }
03942
03943 VectorCopy(cameraCurLoc, camPos);
03944 }
03945
03946 void WP_GetVehicleCamPos( gentity_t *ent, gentity_t *pilot, vec3_t camPos )
03947 {
03948 float thirdPersonHorzOffset = ent->m_pVehicle->m_pVehicleInfo->cameraHorzOffset;
03949 float thirdPersonRange = ent->m_pVehicle->m_pVehicleInfo->cameraRange;
03950 float pitchOffset = ent->m_pVehicle->m_pVehicleInfo->cameraPitchOffset;
03951 float vertOffset = ent->m_pVehicle->m_pVehicleInfo->cameraVertOffset;
03952
03953 if ( ent->client->ps.hackingTime )
03954 {
03955 thirdPersonHorzOffset += (((float)ent->client->ps.hackingTime)/MAX_STRAFE_TIME) * -80.0f;
03956 thirdPersonRange += fabs(((float)ent->client->ps.hackingTime)/MAX_STRAFE_TIME) * 100.0f;
03957 }
03958
03959 if ( ent->m_pVehicle->m_pVehicleInfo->cameraPitchDependantVertOffset )
03960 {
03961 if ( pilot->client->ps.viewangles[PITCH] > 0 )
03962 {
03963 vertOffset = 130+pilot->client->ps.viewangles[PITCH]*-10;
03964 if ( vertOffset < -170 )
03965 {
03966 vertOffset = -170;
03967 }
03968 }
03969 else if ( pilot->client->ps.viewangles[PITCH] < 0 )
03970 {
03971 vertOffset = 130+pilot->client->ps.viewangles[PITCH]*-5;
03972 if ( vertOffset > 130 )
03973 {
03974 vertOffset = 130;
03975 }
03976 }
03977 else
03978 {
03979 vertOffset = 30;
03980 }
03981 if ( pilot->client->ps.viewangles[PITCH] > 0 )
03982 {
03983 pitchOffset = pilot->client->ps.viewangles[PITCH]*-0.75;
03984 }
03985 else if ( pilot->client->ps.viewangles[PITCH] < 0 )
03986 {
03987 pitchOffset = pilot->client->ps.viewangles[PITCH]*-0.75;
03988 }
03989 else
03990 {
03991 pitchOffset = 0;
03992 }
03993 }
03994
03995
03996 G_EstimateCamPos( ent->client->ps.viewangles, pilot->client->ps.origin, pilot->client->ps.viewheight, thirdPersonRange,
03997 thirdPersonHorzOffset, vertOffset, pitchOffset,
03998 pilot->s.number, camPos );
03999
04000
04001
04002
04003
04004
04005 }
04006
04007 void WP_VehLeadCrosshairVeh( gentity_t *camTraceEnt, vec3_t newEnd, const vec3_t dir, const vec3_t shotStart, vec3_t shotDir )
04008 {
04009 if ( camTraceEnt
04010 && camTraceEnt->client
04011 && camTraceEnt->client->NPC_class == CLASS_VEHICLE )
04012 {
04013 float distAdjust = DotProduct( camTraceEnt->client->ps.velocity, dir );
04014 VectorMA( newEnd, distAdjust, dir, newEnd );
04015 }
04016 VectorSubtract( newEnd, shotStart, shotDir );
04017 VectorNormalize( shotDir );
04018 }
04019
04020 #define MAX_XHAIR_DIST_ACCURACY 20000.0f
04021 extern float g_cullDistance;
04022 extern int BG_VehTraceFromCamPos( trace_t *camTrace, bgEntity_t *bgEnt, const vec3_t entOrg, const vec3_t shotStart, const vec3_t end, vec3_t newEnd, vec3_t shotDir, float bestDist );
04023 qboolean WP_VehCheckTraceFromCamPos( gentity_t *ent, const vec3_t shotStart, vec3_t shotDir )
04024 {
04025
04026 if ( !ent
04027 || !ent->m_pVehicle
04028 || !ent->m_pVehicle->m_pVehicleInfo
04029 || !ent->m_pVehicle->m_pPilot
04030 || !((gentity_t*)ent->m_pVehicle->m_pPilot)->client
04031 || (ent->m_pVehicle->m_pPilot->s.number >= MAX_CLIENTS) )
04032 {
04033 return qfalse;
04034 }
04035 if ( (ent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && g_cullDistance > MAX_XHAIR_DIST_ACCURACY )
04036 || ent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER)
04037 {
04038
04039 trace_t trace;
04040 vec3_t dir, start, end;
04041 if ( ent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER )
04042 {
04043 AngleVectors( ent->client->ps.viewangles, dir, NULL, NULL );
04044 VectorCopy( ent->r.currentOrigin, start );
04045 start[2] += ent->m_pVehicle->m_pVehicleInfo->height-DEFAULT_MINS_2-48;
04046 }
04047 else
04048 {
04049 vec3_t ang;
04050 if (ent->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER)
04051 {
04052 VectorSet(ang, 0.0f, ent->m_pVehicle->m_vOrientation[1], 0.0f);
04053 }
04054 else
04055 {
04056 VectorCopy(ent->m_pVehicle->m_vOrientation, ang);
04057 }
04058 AngleVectors( ang, dir, NULL, NULL );
04059 VectorCopy( ent->r.currentOrigin, start );
04060 }
04061 VectorMA( start, g_cullDistance, dir, end );
04062 trap_Trace( &trace, start, vec3_origin, vec3_origin, end,
04063 ent->s.number, CONTENTS_SOLID|CONTENTS_BODY );
04064
04065 if ( ent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER )
04066 {
04067 VectorSubtract( trace.endpos, shotStart, shotDir );
04068 VectorNormalize( shotDir );
04069 return qtrue;
04070 }
04071 else
04072 {
04073 trace_t extraTrace;
04074 vec3_t newEnd;
04075 int camTraceEntNum = BG_VehTraceFromCamPos( &extraTrace, (bgEntity_t *)ent, ent->r.currentOrigin, shotStart, end, newEnd, shotDir, (trace.fraction*g_cullDistance) );
04076 if ( camTraceEntNum )
04077 {
04078 WP_VehLeadCrosshairVeh( &g_entities[camTraceEntNum-1], newEnd, dir, shotStart, shotDir );
04079 return qtrue;
04080 }
04081 }
04082 }
04083 return qfalse;
04084 }
04085
04086
04087 void FireVehicleWeapon( gentity_t *ent, qboolean alt_fire )
04088
04089 {
04090 Vehicle_t *pVeh = ent->m_pVehicle;
04091 int muzzlesFired = 0;
04092 gentity_t *missile = NULL;
04093 vehWeaponInfo_t *vehWeapon = NULL;
04094 qboolean clearRocketLockEntity = qfalse;
04095
04096 if ( !pVeh )
04097 {
04098 return;
04099 }
04100
04101 if (pVeh->m_iRemovedSurfaces)
04102 {
04103 return;
04104 }
04105
04106 if (pVeh->m_pVehicleInfo->type == VH_WALKER &&
04107 ent->client->ps.electrifyTime > level.time)
04108 {
04109 return;
04110 }
04111
04112
04113
04114
04115
04116
04117 if ( pVeh->m_pVehicleInfo &&
04118 (pVeh->m_pVehicleInfo->type != VH_FIGHTER || (pVeh->m_ulFlags&VEH_WINGSOPEN)) )
04119 {
04120 int weaponNum = 0, vehWeaponIndex = VEH_WEAPON_NONE;
04121 int delay = 1000;
04122 qboolean aimCorrect = qfalse;
04123 qboolean linkedFiring = qfalse;
04124
04125 if ( !alt_fire )
04126 {
04127 weaponNum = 0;
04128 }
04129 else
04130 {
04131 weaponNum = 1;
04132 }
04133
04134 vehWeaponIndex = pVeh->m_pVehicleInfo->weapon[weaponNum].ID;
04135
04136 if ( pVeh->weaponStatus[weaponNum].ammo <= 0 )
04137 {
04138 if ( pVeh->m_pPilot && pVeh->m_pPilot->s.number < MAX_CLIENTS )
04139 {
04140 int i;
04141
04142 for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
04143 {
04144 if ( pVeh->m_pVehicleInfo->weapMuzzle[i] != vehWeaponIndex )
04145 {
04146 continue;
04147 }
04148 if ( pVeh->m_iMuzzleTag[i] != -1
04149 && pVeh->m_iMuzzleWait[i] < level.time )
04150 {
04151 G_AddEvent( (gentity_t*)pVeh->m_pPilot, EV_NOAMMO, weaponNum );
04152 break;
04153 }
04154 }
04155 }
04156 return;
04157 }
04158
04159 delay = pVeh->m_pVehicleInfo->weapon[weaponNum].delay;
04160 aimCorrect = pVeh->m_pVehicleInfo->weapon[weaponNum].aimCorrect;
04161 if ( pVeh->m_pVehicleInfo->weapon[weaponNum].linkable == 2
04162 || ( pVeh->m_pVehicleInfo->weapon[weaponNum].linkable == 1
04163 && pVeh->weaponStatus[weaponNum].linked ) )
04164 {
04165 linkedFiring = qtrue;
04166 }
04167
04168 if ( vehWeaponIndex <= VEH_WEAPON_BASE || vehWeaponIndex >= MAX_VEH_WEAPONS )
04169 {
04170 return;
04171 }
04172 else
04173 {
04174 int i, numMuzzles = 0, numMuzzlesReady = 0, cumulativeDelay = 0, cumulativeAmmo = 0;
04175 qboolean sentAmmoWarning = qfalse;
04176
04177 vehWeapon = &g_vehWeaponInfo[vehWeaponIndex];
04178
04179 if ( pVeh->m_pVehicleInfo->weapon[weaponNum].linkable == 2 )
04180 {
04181 cumulativeDelay = delay;
04182 }
04183
04184 for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
04185 {
04186 if ( pVeh->m_pVehicleInfo->weapMuzzle[i] != vehWeaponIndex )
04187 {
04188 continue;
04189 }
04190 if ( pVeh->m_iMuzzleTag[i] != -1 && pVeh->m_iMuzzleWait[i] < level.time )
04191 {
04192 numMuzzlesReady++;
04193 }
04194 if ( pVeh->m_pVehicleInfo->weapMuzzle[pVeh->weaponStatus[weaponNum].nextMuzzle] != vehWeaponIndex )
04195 {
04196
04197 pVeh->weaponStatus[weaponNum].nextMuzzle = i;
04198 }
04199 if ( linkedFiring )
04200 {
04201 cumulativeAmmo += vehWeapon->iAmmoPerShot;
04202 if ( pVeh->m_pVehicleInfo->weapon[weaponNum].linkable != 2 )
04203 {
04204 cumulativeDelay += delay;
04205 }
04206 }
04207 numMuzzles++;
04208 }
04209
04210 if ( linkedFiring )
04211 {
04212 if ( numMuzzlesReady != numMuzzles )
04213 {
04214 return;
04215 }
04216 else
04217 {
04218 if ( pVeh->weaponStatus[weaponNum].ammo < cumulativeAmmo )
04219 {
04220 if ( pVeh->m_pPilot && pVeh->m_pPilot->s.number < MAX_CLIENTS )
04221 {
04222 G_AddEvent( (gentity_t*)pVeh->m_pPilot, EV_NOAMMO, weaponNum );
04223 }
04224 return;
04225 }
04226 }
04227 }
04228
04229 for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
04230 {
04231 if ( pVeh->m_pVehicleInfo->weapMuzzle[i] != vehWeaponIndex )
04232 {
04233 continue;
04234 }
04235 if ( !linkedFiring
04236 && i != pVeh->weaponStatus[weaponNum].nextMuzzle )
04237 {
04238 continue;
04239 }
04240
04241
04242 if ( pVeh->m_iMuzzleTag[i] != -1 && pVeh->m_iMuzzleWait[i] < level.time )
04243 {
04244 vec3_t start, dir;
04245
04246 if ( pVeh->weaponStatus[weaponNum].ammo < vehWeapon->iAmmoPerShot )
04247 {
04248 if ( !sentAmmoWarning )
04249 {
04250 sentAmmoWarning = qtrue;
04251 if ( pVeh->m_pPilot && pVeh->m_pPilot->s.number < MAX_CLIENTS )
04252 {
04253 G_AddEvent( (gentity_t*)pVeh->m_pPilot, EV_NOAMMO, weaponNum );
04254 }
04255 }
04256 }
04257 else
04258 {
04259
04260 WP_CalcVehMuzzle(ent, i);
04261 VectorCopy( pVeh->m_vMuzzlePos[i], start );
04262 VectorCopy( pVeh->m_vMuzzleDir[i], dir );
04263 if ( WP_VehCheckTraceFromCamPos( ent, start, dir ) )
04264 {
04265 }
04266 else if ( aimCorrect )
04267 {
04268 trace_t trace;
04269 vec3_t end;
04270 vec3_t ang;
04271 vec3_t fixedDir;
04272
04273 if (pVeh->m_pVehicleInfo->type == VH_SPEEDER)
04274 {
04275 VectorSet(ang, 0.0f, pVeh->m_vOrientation[1], 0.0f);
04276 }
04277 else
04278 {
04279 VectorCopy(pVeh->m_vOrientation, ang);
04280 }
04281 AngleVectors( ang, fixedDir, NULL, NULL );
04282 VectorMA( ent->r.currentOrigin, 32768, fixedDir, end );
04283
04284 trap_Trace( &trace, ent->r.currentOrigin, vec3_origin, vec3_origin, end, ent->s.number, MASK_SHOT );
04285 if ( trace.fraction < 1.0f && !trace.allsolid && !trace.startsolid )
04286 {
04287 vec3_t newEnd;
04288 VectorCopy( trace.endpos, newEnd );
04289 WP_VehLeadCrosshairVeh( &g_entities[trace.entityNum], newEnd, fixedDir, start, dir );
04290 }
04291 }
04292
04293
04294
04295 muzzlesFired |= (1<<i);
04296
04297 missile = WP_FireVehicleWeapon( ent, start, dir, vehWeapon, alt_fire, qfalse );
04298 if ( vehWeapon->fHoming )
04299 {
04300 clearRocketLockEntity = qtrue;
04301 }
04302 }
04303
04304 if ( linkedFiring )
04305 {
04306 continue;
04307 }
04308
04309
04310 if ( numMuzzles > 1 )
04311 {
04312 int nextMuzzle = pVeh->weaponStatus[weaponNum].nextMuzzle;
04313 while ( 1 )
04314 {
04315 nextMuzzle++;
04316 if ( nextMuzzle >= MAX_VEHICLE_MUZZLES )
04317 {
04318 nextMuzzle = 0;
04319 }
04320 if ( nextMuzzle == pVeh->weaponStatus[weaponNum].nextMuzzle )
04321 {
04322 break;
04323 }
04324 if ( pVeh->m_pVehicleInfo->weapMuzzle[nextMuzzle] == vehWeaponIndex )
04325 {
04326 pVeh->weaponStatus[weaponNum].nextMuzzle = nextMuzzle;
04327 break;
04328 }
04329 }
04330 }
04331
04332 pVeh->m_iMuzzleWait[pVeh->weaponStatus[weaponNum].nextMuzzle] = level.time + delay;
04333
04334 pVeh->weaponStatus[weaponNum].ammo -= vehWeapon->iAmmoPerShot;
04335
04336 if ( pVeh->m_pParentEntity && ((gentity_t*)(pVeh->m_pParentEntity))->client )
04337 {
04338 ((gentity_t*)(pVeh->m_pParentEntity))->client->ps.ammo[weaponNum] = pVeh->weaponStatus[weaponNum].ammo;
04339 }
04340
04341
04342
04343 goto tryFire;
04344 }
04345 }
04346
04347 if ( cumulativeAmmo )
04348 {
04349
04350 pVeh->weaponStatus[weaponNum].ammo -= cumulativeAmmo;
04351
04352 if ( pVeh->m_pParentEntity && ((gentity_t*)(pVeh->m_pParentEntity))->client )
04353 {
04354 ((gentity_t*)(pVeh->m_pParentEntity))->client->ps.ammo[weaponNum] = pVeh->weaponStatus[weaponNum].ammo;
04355 }
04356 }
04357 if ( cumulativeDelay )
04358 {
04359 for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
04360 {
04361 if ( pVeh->m_pVehicleInfo->weapMuzzle[i] != vehWeaponIndex )
04362 {
04363 continue;
04364 }
04365
04366 pVeh->m_iMuzzleWait[i] = level.time + cumulativeDelay;
04367 }
04368 }
04369 }
04370 }
04371
04372 tryFire:
04373 if ( clearRocketLockEntity )
04374 {
04375 ent->client->ps.rocketLockIndex = ENTITYNUM_NONE;
04376 ent->client->ps.rocketLockTime = 0;
04377 ent->client->ps.rocketTargetTime = 0;
04378 }
04379
04380 if ( vehWeapon && muzzlesFired > 0 )
04381 {
04382 G_VehMuzzleFireFX(ent, missile, muzzlesFired );
04383 }
04384 }
04385
04386
04387
04388
04389
04390
04391 #include "../namespace_begin.h"
04392 int BG_EmplacedView(vec3_t baseAngles, vec3_t angles, float *newYaw, float constraint);
04393 #include "../namespace_end.h"
04394
04395 void FireWeapon( gentity_t *ent, qboolean altFire ) {
04396 if (ent->client->ps.powerups[PW_QUAD] ) {
04397 s_quadFactor = g_quadfactor.value;
04398 } else {
04399 s_quadFactor = 1;
04400 }
04401
04402
04403 if( ent->s.weapon != WP_SABER && ent->s.weapon != WP_STUN_BATON && ent->s.weapon != WP_MELEE )
04404 {
04405 if( ent->s.weapon == WP_FLECHETTE ) {
04406 ent->client->accuracy_shots += FLECHETTE_SHOTS;
04407 } else {
04408 ent->client->accuracy_shots++;
04409 }
04410 }
04411
04412 if ( ent && ent->client && ent->client->NPC_class == CLASS_VEHICLE )
04413 {
04414 FireVehicleWeapon( ent, altFire );
04415 return;
04416 }
04417 else
04418 {
04419
04420 if (ent->s.weapon == WP_EMPLACED_GUN &&
04421 ent->client->ps.emplacedIndex)
04422 {
04423 gentity_t *emp = &g_entities[ent->client->ps.emplacedIndex];
04424
04425 if (emp->inuse)
04426 {
04427 float yaw;
04428 vec3_t viewAngCap;
04429 int override;
04430
04431 VectorCopy(ent->client->ps.viewangles, viewAngCap);
04432 if (viewAngCap[PITCH] > 40)
04433 {
04434 viewAngCap[PITCH] = 40;
04435 }
04436
04437 override = BG_EmplacedView(ent->client->ps.viewangles, emp->s.angles, &yaw,
04438 emp->s.origin2[0]);
04439
04440 if (override)
04441 {
04442 viewAngCap[YAW] = yaw;
04443 }
04444
04445 AngleVectors( viewAngCap, forward, vright, up );
04446 }
04447 else
04448 {
04449 AngleVectors( ent->client->ps.viewangles, forward, vright, up );
04450 }
04451 }
04452 else if (ent->s.number < MAX_CLIENTS &&
04453 ent->client->ps.m_iVehicleNum && ent->s.weapon == WP_BLASTER)
04454 {
04455 vec3_t vehTurnAngles;
04456 gentity_t *vehEnt = &g_entities[ent->client->ps.m_iVehicleNum];
04457
04458 if (vehEnt->inuse && vehEnt->client && vehEnt->m_pVehicle)
04459 {
04460 VectorCopy(vehEnt->m_pVehicle->m_vOrientation, vehTurnAngles);
04461 vehTurnAngles[PITCH] = ent->client->ps.viewangles[PITCH];
04462 }
04463 else
04464 {
04465 VectorCopy(ent->client->ps.viewangles, vehTurnAngles);
04466 }
04467 if (ent->client->pers.cmd.rightmove > 0)
04468 {
04469 vehTurnAngles[YAW] -= 90.0f;
04470 }
04471 else if (ent->client->pers.cmd.rightmove < 0)
04472 {
04473 vehTurnAngles[YAW] += 90.0f;
04474 }
04475
04476 AngleVectors( vehTurnAngles, forward, vright, up );
04477 }
04478 else
04479 {
04480 AngleVectors( ent->client->ps.viewangles, forward, vright, up );
04481 }
04482
04483 CalcMuzzlePoint ( ent, forward, vright, up, muzzle );
04484
04485
04486 switch( ent->s.weapon ) {
04487 case WP_STUN_BATON:
04488 WP_FireStunBaton( ent, altFire );
04489 break;
04490
04491 case WP_MELEE:
04492 WP_FireMelee(ent, altFire);
04493 break;
04494
04495 case WP_SABER:
04496 break;
04497
04498 case WP_BRYAR_PISTOL:
04499
04500 if (1)
04501 {
04502 WP_FireBryarPistol( ent, altFire );
04503 }
04504 else
04505 {
04506 WP_FireBryarPistol( ent, qfalse );
04507 }
04508 break;
04509
04510 case WP_CONCUSSION:
04511 if ( altFire )
04512 {
04513 WP_FireConcussionAlt( ent );
04514 }
04515 else
04516 {
04517 WP_FireConcussion( ent );
04518 }
04519 break;
04520
04521 case WP_BRYAR_OLD:
04522 WP_FireBryarPistol( ent, altFire );
04523 break;
04524
04525 case WP_BLASTER:
04526 WP_FireBlaster( ent, altFire );
04527 break;
04528
04529 case WP_DISRUPTOR:
04530 WP_FireDisruptor( ent, altFire );
04531 break;
04532
04533 case WP_BOWCASTER:
04534 WP_FireBowcaster( ent, altFire );
04535 break;
04536
04537 case WP_REPEATER:
04538 WP_FireRepeater( ent, altFire );
04539 break;
04540
04541 case WP_DEMP2:
04542 WP_FireDEMP2( ent, altFire );
04543 break;
04544
04545 case WP_FLECHETTE:
04546 WP_FireFlechette( ent, altFire );
04547 break;
04548
04549 case WP_ROCKET_LAUNCHER:
04550 WP_FireRocket( ent, altFire );
04551 break;
04552
04553 case WP_THERMAL:
04554 WP_FireThermalDetonator( ent, altFire );
04555 break;
04556
04557 case WP_TRIP_MINE:
04558 WP_PlaceLaserTrap( ent, altFire );
04559 break;
04560
04561 case WP_DET_PACK:
04562 WP_DropDetPack( ent, altFire );
04563 break;
04564
04565 case WP_EMPLACED_GUN:
04566 if (ent->client && ent->client->ewebIndex)
04567 {
04568 break;
04569 }
04570 WP_FireEmplaced( ent, altFire );
04571 break;
04572 default:
04573
04574 break;
04575 }
04576 }
04577
04578 G_LogWeaponFire(ent->s.number, ent->s.weapon);
04579 }
04580
04581
04582 static void WP_FireEmplaced( gentity_t *ent, qboolean altFire )
04583
04584 {
04585 vec3_t dir, angs, gunpoint;
04586 vec3_t right;
04587 gentity_t *gun;
04588 int side;
04589
04590 if (!ent->client)
04591 {
04592 return;
04593 }
04594
04595 if (!ent->client->ps.emplacedIndex)
04596 {
04597 return;
04598 }
04599
04600 gun = &g_entities[ent->client->ps.emplacedIndex];
04601
04602 if (!gun->inuse || gun->health <= 0)
04603 {
04604 return;
04605 }
04606
04607 VectorCopy(gun->s.origin, gunpoint);
04608 gunpoint[2] += 46;
04609
04610 AngleVectors(ent->client->ps.viewangles, NULL, right, NULL);
04611
04612 if (gun->genericValue10)
04613 {
04614 VectorMA(gunpoint, 10.0f, right, gunpoint);
04615 side = 0;
04616 }
04617 else
04618 {
04619 VectorMA(gunpoint, -10.0f, right, gunpoint);
04620 side = 1;
04621 }
04622
04623 gun->genericValue10 = side;
04624 G_AddEvent(gun, EV_FIRE_WEAPON, side);
04625
04626 vectoangles( forward, angs );
04627
04628 AngleVectors( angs, dir, NULL, NULL );
04629
04630 WP_FireEmplacedMissile( gun, gunpoint, dir, altFire, ent );
04631 }
04632
04633 #define EMPLACED_CANRESPAWN 1
04634
04635
04636
04637
04638
04639
04640
04641
04642
04643
04644
04645
04646
04647
04648
04649
04650
04651
04652
04653
04654
04655
04656
04657
04658
04659
04660
04661 extern qboolean TryHeal(gentity_t *ent, gentity_t *target);
04662 void emplaced_gun_use( gentity_t *self, gentity_t *other, trace_t *trace )
04663 {
04664 vec3_t fwd1, fwd2;
04665 float dot;
04666 int oldWeapon;
04667 gentity_t *activator = other;
04668 float zoffset = 50;
04669 vec3_t anglesToOwner;
04670 vec3_t vLen;
04671 float ownLen;
04672
04673 if ( self->health <= 0 )
04674 {
04675 return;
04676 }
04677
04678 if (self->activator)
04679 {
04680 return;
04681 }
04682
04683 if (!activator->client)
04684 {
04685 return;
04686 }
04687
04688 if (activator->client->ps.emplacedTime > level.time)
04689 {
04690 return;
04691 }
04692
04693 if (activator->client->ps.forceHandExtend != HANDEXTEND_NONE)
04694 {
04695 return;
04696 }
04697
04698 if (activator->client->ps.origin[2] > self->s.origin[2]+zoffset-8)
04699 {
04700 return;
04701 }
04702
04703 if (activator->client->ps.pm_flags & PMF_DUCKED)
04704 {
04705 return;
04706 }
04707
04708 if (activator->client->ps.isJediMaster)
04709 {
04710 return;
04711 }
04712
04713 VectorSubtract(self->s.origin, activator->client->ps.origin, vLen);
04714 ownLen = VectorLength(vLen);
04715
04716 if (ownLen > 64.0f)
04717 {
04718 return;
04719 }
04720
04721
04722 AngleVectors( activator->client->ps.viewangles, fwd1, NULL, NULL );
04723
04724
04725 AngleVectors( self->pos1, fwd2, NULL, NULL );
04726
04727 dot = DotProduct( fwd1, fwd2 );
04728
04729
04730 if ( dot < -0.2f )
04731 {
04732 goto tryHeal;
04733 }
04734
04735 VectorSubtract(self->s.origin, activator->client->ps.origin, fwd1);
04736 VectorNormalize(fwd1);
04737
04738 dot = DotProduct( fwd1, fwd2 );
04739
04740
04741 if ( dot < 0.6f )
04742 {
04743 goto tryHeal;
04744 }
04745
04746 self->genericValue1 = 1;
04747
04748 oldWeapon = activator->s.weapon;
04749
04750
04751 activator->client->ps.weapon = self->s.weapon;
04752 activator->client->ps.weaponstate = WEAPON_READY;
04753 activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN );
04754
04755 activator->client->ps.emplacedIndex = self->s.number;
04756
04757 self->s.emplacedOwner = activator->s.number;
04758 self->s.activeForcePass = NUM_FORCE_POWERS+1;
04759
04760
04761 self->s.weapon = oldWeapon;
04762
04763
04764 activator->r.ownerNum = self->s.number;
04765 self->activator = activator;
04766
04767 VectorSubtract(self->r.currentOrigin, activator->client->ps.origin, anglesToOwner);
04768 vectoangles(anglesToOwner, anglesToOwner);
04769 return;
04770
04771 tryHeal:
04772 TryHeal(activator, self);
04773 }
04774
04775 void emplaced_gun_realuse( gentity_t *self, gentity_t *other, gentity_t *activator )
04776 {
04777 emplaced_gun_use(self, other, NULL);
04778 }
04779
04780
04781 void emplaced_gun_pain( gentity_t *self, gentity_t *attacker, int damage )
04782 {
04783 self->s.health = self->health;
04784
04785 if ( self->health <= 0 )
04786 {
04787
04788 }
04789 else
04790 {
04791
04792 G_ActivateBehavior( self, BSET_PAIN );
04793 }
04794 }
04795
04796 #define EMPLACED_GUN_HEALTH 800
04797
04798
04799 void emplaced_gun_update(gentity_t *self)
04800 {
04801 vec3_t smokeOrg, puffAngle;
04802 int oldWeap;
04803 float ownLen = 0;
04804
04805 if (self->health < 1 && !self->genericValue5)
04806 {
04807 if (self->spawnflags & EMPLACED_CANRESPAWN)
04808 {
04809 self->genericValue5 = level.time + 4000 + self->count;
04810 }
04811 }
04812 else if (self->health < 1 && self->genericValue5 < level.time)
04813 {
04814 self->s.time = 0;
04815 self->genericValue4 = 0;
04816 self->genericValue3 = 0;
04817 self->health = EMPLACED_GUN_HEALTH*0.4;
04818 self->s.health = self->health;
04819 }
04820
04821 if (self->genericValue4 && self->genericValue4 < 2 && self->s.time < level.time)
04822 {
04823 vec3_t explOrg;
04824
04825 VectorSet( puffAngle, 0, 0, 1 );
04826
04827 VectorCopy(self->r.currentOrigin, explOrg);
04828 explOrg[2] += 16;
04829
04830
04831 G_PlayEffect(EFFECT_EXPLOSION_DETPACK, explOrg, puffAngle);
04832
04833 self->genericValue3 = level.time + Q_irand(2500, 3500);
04834
04835 G_RadiusDamage(self->r.currentOrigin, self, self->splashDamage, self->splashRadius, self, NULL, MOD_UNKNOWN);
04836
04837 self->s.time = -1;
04838
04839 self->genericValue4 = 2;
04840 }
04841
04842 if (self->genericValue3 > level.time)
04843 {
04844 if (self->genericValue2 < level.time)
04845 {
04846 VectorSet( puffAngle, 0, 0, 1 );
04847 VectorCopy(self->r.currentOrigin, smokeOrg);
04848
04849 smokeOrg[2] += 60;
04850
04851 G_PlayEffect(EFFECT_SMOKE, smokeOrg, puffAngle);
04852 self->genericValue2 = level.time + Q_irand(250, 400);
04853 }
04854 }
04855
04856 if (self->activator && self->activator->client && self->activator->inuse)
04857 {
04858 vec3_t vLen;
04859 VectorSubtract(self->s.origin, self->activator->client->ps.origin, vLen);
04860 ownLen = VectorLength(vLen);
04861
04862 if (!(self->activator->client->pers.cmd.buttons & BUTTON_USE) && self->genericValue1)
04863 {
04864 self->genericValue1 = 0;
04865 }
04866
04867 if ((self->activator->client->pers.cmd.buttons & BUTTON_USE) && !self->genericValue1)
04868 {
04869 self->activator->client->ps.emplacedIndex = 0;
04870 self->activator->client->ps.saberHolstered = 0;
04871 self->nextthink = level.time + 50;
04872 return;
04873 }
04874 }
04875
04876 if ((self->activator && self->activator->client) &&
04877 (!self->activator->inuse || self->activator->client->ps.emplacedIndex != self->s.number || self->genericValue4 || ownLen > 64))
04878 {
04879 self->activator->client->ps.stats[STAT_WEAPONS] &= ~(1<<WP_EMPLACED_GUN);
04880
04881 oldWeap = self->activator->client->ps.weapon;
04882 self->activator->client->ps.weapon = self->s.weapon;
04883 self->s.weapon = oldWeap;
04884 self->activator->r.ownerNum = ENTITYNUM_NONE;
04885 self->activator->client->ps.emplacedTime = level.time + 1000;
04886 self->activator->client->ps.emplacedIndex = 0;
04887 self->activator->client->ps.saberHolstered = 0;
04888 self->activator = NULL;
04889
04890 self->s.activeForcePass = 0;
04891 }
04892 else if (self->activator && self->activator->client)
04893 {
04894 self->activator->client->ps.weapon = WP_EMPLACED_GUN;
04895 self->activator->client->ps.weaponstate = WEAPON_READY;
04896 }
04897 self->nextthink = level.time + 50;
04898 }
04899
04900
04901 void emplaced_gun_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod )
04902 {
04903 if (self->genericValue4)
04904 {
04905 return;
04906 }
04907
04908 self->genericValue4 = 1;
04909
04910 self->s.time = level.time + 3000;
04911
04912 self->genericValue5 = 0;
04913 }
04914
04915 void SP_emplaced_gun( gentity_t *ent )
04916 {
04917 const char *name = "models/map_objects/mp/turret_chair.glm";
04918 vec3_t down;
04919 trace_t tr;
04920
04921
04922 RegisterItem( BG_FindItemForWeapon(WP_EMPLACED_GUN) );
04923
04924 ent->r.contents = CONTENTS_SOLID;
04925 ent->s.solid = SOLID_BBOX;
04926
04927 ent->genericValue5 = 0;
04928
04929 VectorSet( ent->r.mins, -30, -20, 8 );
04930 VectorSet( ent->r.maxs, 30, 20, 60 );
04931
04932 VectorCopy(ent->s.origin, down);
04933
04934 down[2] -= 1024;
04935
04936 trap_Trace(&tr, ent->s.origin, ent->r.mins, ent->r.maxs, down, ent->s.number, MASK_SOLID);
04937
04938 if (tr.fraction != 1 && !tr.allsolid && !tr.startsolid)
04939 {
04940 VectorCopy(tr.endpos, ent->s.origin);
04941 }
04942
04943 ent->spawnflags |= 4;
04944
04945 ent->health = EMPLACED_GUN_HEALTH;
04946
04947 if (ent->spawnflags & EMPLACED_CANRESPAWN)
04948 {
04949 ent->health *= 0.4;
04950 }
04951
04952 ent->maxHealth = ent->health;
04953 G_ScaleNetHealth(ent);
04954
04955 ent->genericValue4 = 0;
04956
04957 ent->takedamage = qtrue;
04958 ent->pain = emplaced_gun_pain;
04959 ent->die = emplaced_gun_die;
04960
04961
04962 ent->splashDamage = 80;
04963 ent->splashRadius = 128;
04964
04965
04966 G_SpawnInt( "count", "600", &ent->count );
04967
04968 G_SpawnFloat( "constraint", "60", &ent->s.origin2[0] );
04969
04970 ent->s.modelindex = G_ModelIndex( (char *)name );
04971 ent->s.modelGhoul2 = 1;
04972 ent->s.g2radius = 110;
04973
04974
04975 ent->s.weapon = WP_EMPLACED_GUN;
04976
04977 G_SetOrigin( ent, ent->s.origin );
04978
04979
04980 VectorCopy( ent->s.angles, ent->pos1 );
04981 VectorCopy( ent->s.angles, ent->r.currentAngles );
04982 VectorCopy( ent->s.angles, ent->s.apos.trBase );
04983
04984 ent->think = emplaced_gun_update;
04985 ent->nextthink = level.time + 50;
04986
04987 ent->use = emplaced_gun_realuse;
04988
04989 ent->r.svFlags |= SVF_PLAYER_USABLE;
04990
04991 ent->s.pos.trType = TR_STATIONARY;
04992
04993 ent->s.owner = MAX_CLIENTS+1;
04994 ent->s.shouldtarget = qtrue;
04995
04996
04997 trap_LinkEntity(ent);
04998 }