2
===========================================================================
3
Copyright (C) 1999-2005 Id Software, Inc.
5
This file is part of Quake III Arena source code.
7
Quake III Arena source code is free software; you can redistribute it
8
and/or modify it under the terms of the GNU General Public License as
9
published by the Free Software Foundation; either version 2 of the License,
10
or (at your option) any later version.
12
Quake III Arena source code is distributed in the hope that it will be
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with Quake III Arena source code; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
===========================================================================
24
// perform the server side effects of a weapon firing
28
static float s_quadFactor;
29
static vec3_t forward, right, up;
32
#define NUM_NAILSHOTS 15
39
void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) {
43
VectorSubtract( impact, start, v );
44
dot = DotProduct( v, dir );
45
VectorMA( v, -2*dot, dir, newv );
47
VectorNormalize(newv);
48
VectorMA(impact, 8192, newv, endout);
53
======================================================================
57
======================================================================
60
void Weapon_Gauntlet( gentity_t *ent ) {
69
qboolean CheckGauntletAttack( gentity_t *ent ) {
76
// set aiming directions
77
AngleVectors (ent->client->ps.viewangles, forward, right, up);
79
CalcMuzzlePoint ( ent, forward, right, up, muzzle );
81
VectorMA (muzzle, 32, forward, end);
83
trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
84
if ( tr.surfaceFlags & SURF_NOIMPACT ) {
88
traceEnt = &g_entities[ tr.entityNum ];
91
if ( traceEnt->takedamage && traceEnt->client ) {
92
tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
93
tent->s.otherEntityNum = traceEnt->s.number;
94
tent->s.eventParm = DirToByte( tr.plane.normal );
95
tent->s.weapon = ent->s.weapon;
98
if ( !traceEnt->takedamage) {
102
if (ent->client->ps.powerups[PW_QUAD] ) {
103
G_AddEvent( ent, EV_POWERUP_QUAD, 0 );
104
s_quadFactor = g_quadfactor.value;
109
if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
114
damage = 50 * s_quadFactor;
115
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
116
damage, 0, MOD_GAUNTLET );
123
======================================================================
127
======================================================================
131
======================
134
Round a vector to integers for more efficient network
135
transmission, but make sure that it rounds towards a given point
136
rather than blindly truncating. This prevents it from truncating
138
======================
140
void SnapVectorTowards( vec3_t v, vec3_t to ) {
143
for ( i = 0 ; i < 3 ; i++ ) {
144
if ( to[i] <= v[i] ) {
147
v[i] = (int)v[i] + 1;
153
#define CHAINGUN_SPREAD 600
155
#define MACHINEGUN_SPREAD 200
156
#define MACHINEGUN_DAMAGE 7
157
#define MACHINEGUN_TEAM_DAMAGE 5 // wimpier MG in teamplay
159
void Bullet_Fire (gentity_t *ent, float spread, int damage ) {
163
vec3_t impactpoint, bouncedir;
171
damage *= s_quadFactor;
173
r = random() * M_PI * 2.0f;
174
u = sin(r) * crandom() * spread * 16;
175
r = cos(r) * crandom() * spread * 16;
176
VectorMA (muzzle, 8192*16, forward, end);
177
VectorMA (end, r, right, end);
178
VectorMA (end, u, up, end);
180
passent = ent->s.number;
181
for (i = 0; i < 10; i++) {
183
trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);
184
if ( tr.surfaceFlags & SURF_NOIMPACT ) {
188
traceEnt = &g_entities[ tr.entityNum ];
190
// snap the endpos to integers, but nudged towards the line
191
SnapVectorTowards( tr.endpos, muzzle );
193
// send bullet impact
194
if ( traceEnt->takedamage && traceEnt->client ) {
195
tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH );
196
tent->s.eventParm = traceEnt->s.number;
197
if( LogAccuracyHit( traceEnt, ent ) ) {
198
ent->client->accuracy_hits++;
201
tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL );
202
tent->s.eventParm = DirToByte( tr.plane.normal );
204
tent->s.otherEntityNum = ent->s.number;
206
if ( traceEnt->takedamage) {
208
if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
209
if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
210
G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
211
VectorCopy( impactpoint, muzzle );
212
// the player can hit him/herself with the bounced rail
213
passent = ENTITYNUM_NONE;
216
VectorCopy( tr.endpos, muzzle );
217
passent = traceEnt->s.number;
223
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
224
damage, 0, MOD_MACHINEGUN);
235
======================================================================
239
======================================================================
242
void BFG_Fire ( gentity_t *ent ) {
245
m = fire_bfg (ent, muzzle, forward);
246
m->damage *= s_quadFactor;
247
m->splashDamage *= s_quadFactor;
249
// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
254
======================================================================
258
======================================================================
261
// DEFAULT_SHOTGUN_SPREAD and DEFAULT_SHOTGUN_COUNT are in bg_public.h, because
262
// client predicts same spreads
263
#define DEFAULT_SHOTGUN_DAMAGE 10
265
qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) {
267
int damage, i, passent;
270
vec3_t impactpoint, bouncedir;
272
vec3_t tr_start, tr_end;
274
passent = ent->s.number;
275
VectorCopy( start, tr_start );
276
VectorCopy( end, tr_end );
277
for (i = 0; i < 10; i++) {
278
trap_Trace (&tr, tr_start, NULL, NULL, tr_end, passent, MASK_SHOT);
279
traceEnt = &g_entities[ tr.entityNum ];
281
// send bullet impact
282
if ( tr.surfaceFlags & SURF_NOIMPACT ) {
286
if ( traceEnt->takedamage) {
287
damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor;
289
if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
290
if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
291
G_BounceProjectile( tr_start, impactpoint, bouncedir, tr_end );
292
VectorCopy( impactpoint, tr_start );
293
// the player can hit him/herself with the bounced rail
294
passent = ENTITYNUM_NONE;
297
VectorCopy( tr.endpos, tr_start );
298
passent = traceEnt->s.number;
303
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
304
damage, 0, MOD_SHOTGUN);
305
if( LogAccuracyHit( traceEnt, ent ) ) {
310
G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN);
311
if( LogAccuracyHit( traceEnt, ent ) ) {
321
// this should match CG_ShotgunPattern
322
void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) {
326
vec3_t forward, right, up;
328
qboolean hitClient = qfalse;
330
// derive the right and up vectors from the forward vector, because
331
// the client won't have any other information
332
VectorNormalize2( origin2, forward );
333
PerpendicularVector( right, forward );
334
CrossProduct( forward, right, up );
336
oldScore = ent->client->ps.persistant[PERS_SCORE];
338
// generate the "random" spread pattern
339
for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) {
340
r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
341
u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
342
VectorMA( origin, 8192 * 16, forward, end);
343
VectorMA (end, r, right, end);
344
VectorMA (end, u, up, end);
345
if( ShotgunPellet( origin, end, ent ) && !hitClient ) {
347
ent->client->accuracy_hits++;
353
void weapon_supershotgun_fire (gentity_t *ent) {
356
// send shotgun blast
357
tent = G_TempEntity( muzzle, EV_SHOTGUN );
358
VectorScale( forward, 4096, tent->s.origin2 );
359
SnapVector( tent->s.origin2 );
360
tent->s.eventParm = rand() & 255; // seed for spread pattern
361
tent->s.otherEntityNum = ent->s.number;
363
ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
368
======================================================================
372
======================================================================
375
void weapon_grenadelauncher_fire (gentity_t *ent) {
378
// extra vertical velocity
380
VectorNormalize( forward );
382
m = fire_grenade (ent, muzzle, forward);
383
m->damage *= s_quadFactor;
384
m->splashDamage *= s_quadFactor;
386
// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
390
======================================================================
394
======================================================================
397
void Weapon_RocketLauncher_Fire (gentity_t *ent) {
400
m = fire_rocket (ent, muzzle, forward);
401
m->damage *= s_quadFactor;
402
m->splashDamage *= s_quadFactor;
404
// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
409
======================================================================
413
======================================================================
416
void Weapon_Plasmagun_Fire (gentity_t *ent) {
419
m = fire_plasma (ent, muzzle, forward);
420
m->damage *= s_quadFactor;
421
m->splashDamage *= s_quadFactor;
423
// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
427
======================================================================
431
======================================================================
440
#define MAX_RAIL_HITS 4
441
void weapon_railgun_fire (gentity_t *ent) {
444
vec3_t impactpoint, bouncedir;
454
gentity_t *unlinkedEntities[MAX_RAIL_HITS];
456
damage = 100 * s_quadFactor;
458
VectorMA (muzzle, 8192, forward, end);
460
// trace only against the solids, so the railgun will go through people
463
passent = ent->s.number;
465
trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT );
466
if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) {
469
traceEnt = &g_entities[ trace.entityNum ];
470
if ( traceEnt->takedamage ) {
472
if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
473
if ( G_InvulnerabilityEffect( traceEnt, forward, trace.endpos, impactpoint, bouncedir ) ) {
474
G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
475
// snap the endpos to integers to save net bandwidth, but nudged towards the line
476
SnapVectorTowards( trace.endpos, muzzle );
477
// send railgun beam effect
478
tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );
479
// set player number for custom colors on the railtrail
480
tent->s.clientNum = ent->s.clientNum;
481
VectorCopy( muzzle, tent->s.origin2 );
482
// move origin a bit to come closer to the drawn gun muzzle
483
VectorMA( tent->s.origin2, 4, right, tent->s.origin2 );
484
VectorMA( tent->s.origin2, -1, up, tent->s.origin2 );
485
tent->s.eventParm = 255; // don't make the explosion at the end
487
VectorCopy( impactpoint, muzzle );
488
// the player can hit him/herself with the bounced rail
489
passent = ENTITYNUM_NONE;
493
if( LogAccuracyHit( traceEnt, ent ) ) {
496
G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
499
if( LogAccuracyHit( traceEnt, ent ) ) {
502
G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
505
if ( trace.contents & CONTENTS_SOLID ) {
506
break; // we hit something solid enough to stop the beam
508
// unlink this entity, so the next trace will go past it
509
trap_UnlinkEntity( traceEnt );
510
unlinkedEntities[unlinked] = traceEnt;
512
} while ( unlinked < MAX_RAIL_HITS );
514
// link back in any entities we unlinked
515
for ( i = 0 ; i < unlinked ; i++ ) {
516
trap_LinkEntity( unlinkedEntities[i] );
519
// the final trace endpos will be the terminal point of the rail trail
521
// snap the endpos to integers to save net bandwidth, but nudged towards the line
522
SnapVectorTowards( trace.endpos, muzzle );
524
// send railgun beam effect
525
tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );
527
// set player number for custom colors on the railtrail
528
tent->s.clientNum = ent->s.clientNum;
530
VectorCopy( muzzle, tent->s.origin2 );
531
// move origin a bit to come closer to the drawn gun muzzle
532
VectorMA( tent->s.origin2, 4, right, tent->s.origin2 );
533
VectorMA( tent->s.origin2, -1, up, tent->s.origin2 );
535
// no explosion at end if SURF_NOIMPACT, but still make the trail
536
if ( trace.surfaceFlags & SURF_NOIMPACT ) {
537
tent->s.eventParm = 255; // don't make the explosion at the end
539
tent->s.eventParm = DirToByte( trace.plane.normal );
541
tent->s.clientNum = ent->s.clientNum;
543
// give the shooter a reward sound if they have made two railgun hits in a row
546
ent->client->accurateCount = 0;
548
// check for "impressive" reward sound
549
ent->client->accurateCount += hits;
550
if ( ent->client->accurateCount >= 2 ) {
551
ent->client->accurateCount -= 2;
552
ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
553
// add the sprite over the player's head
554
ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
555
ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
556
ent->client->rewardTime = level.time + REWARD_SPRITE_TIME;
558
ent->client->accuracy_hits++;
565
======================================================================
569
======================================================================
572
void Weapon_GrapplingHook_Fire (gentity_t *ent)
574
if (!ent->client->fireHeld && !ent->client->hook)
575
fire_grapple (ent, muzzle, forward);
577
ent->client->fireHeld = qtrue;
580
void Weapon_HookFree (gentity_t *ent)
582
ent->parent->client->hook = NULL;
583
ent->parent->client->ps.pm_flags &= ~PMF_GRAPPLE_PULL;
587
void Weapon_HookThink (gentity_t *ent)
592
VectorCopy(ent->r.currentOrigin, oldorigin);
593
v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->r.mins[0] + ent->enemy->r.maxs[0]) * 0.5;
594
v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->r.mins[1] + ent->enemy->r.maxs[1]) * 0.5;
595
v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5;
596
SnapVectorTowards( v, oldorigin ); // save net bandwidth
598
G_SetOrigin( ent, v );
601
VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);
605
======================================================================
609
======================================================================
612
void Weapon_LightningFire( gentity_t *ent ) {
616
vec3_t impactpoint, bouncedir;
618
gentity_t *traceEnt, *tent;
619
int damage, i, passent;
621
damage = 8 * s_quadFactor;
623
passent = ent->s.number;
624
for (i = 0; i < 10; i++) {
625
VectorMA( muzzle, LIGHTNING_RANGE, forward, end );
627
trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT );
630
// if not the first trace (the lightning bounced of an invulnerability sphere)
632
// add bounced off lightning bolt temp entity
633
// the first lightning bolt is a cgame only visual
635
tent = G_TempEntity( muzzle, EV_LIGHTNINGBOLT );
636
VectorCopy( tr.endpos, end );
638
VectorCopy( end, tent->s.origin2 );
641
if ( tr.entityNum == ENTITYNUM_NONE ) {
645
traceEnt = &g_entities[ tr.entityNum ];
647
if ( traceEnt->takedamage) {
649
if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
650
if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
651
G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
652
VectorCopy( impactpoint, muzzle );
653
VectorSubtract( end, impactpoint, forward );
654
VectorNormalize(forward);
655
// the player can hit him/herself with the bounced lightning
656
passent = ENTITYNUM_NONE;
659
VectorCopy( tr.endpos, muzzle );
660
passent = traceEnt->s.number;
665
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
666
damage, 0, MOD_LIGHTNING);
669
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
670
damage, 0, MOD_LIGHTNING);
674
if ( traceEnt->takedamage && traceEnt->client ) {
675
tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
676
tent->s.otherEntityNum = traceEnt->s.number;
677
tent->s.eventParm = DirToByte( tr.plane.normal );
678
tent->s.weapon = ent->s.weapon;
679
if( LogAccuracyHit( traceEnt, ent ) ) {
680
ent->client->accuracy_hits++;
682
} else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
683
tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
684
tent->s.eventParm = DirToByte( tr.plane.normal );
693
======================================================================
697
======================================================================
700
void Weapon_Nailgun_Fire (gentity_t *ent) {
704
for( count = 0; count < NUM_NAILSHOTS; count++ ) {
705
m = fire_nail (ent, muzzle, forward, right, up );
706
m->damage *= s_quadFactor;
707
m->splashDamage *= s_quadFactor;
710
// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
715
======================================================================
717
PROXIMITY MINE LAUNCHER
719
======================================================================
722
void weapon_proxlauncher_fire (gentity_t *ent) {
725
// extra vertical velocity
727
VectorNormalize( forward );
729
m = fire_prox (ent, muzzle, forward);
730
m->damage *= s_quadFactor;
731
m->splashDamage *= s_quadFactor;
733
// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
738
//======================================================================
746
qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
747
if( !target->takedamage ) {
751
if ( target == attacker ) {
755
if( !target->client ) {
759
if( !attacker->client ) {
763
if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
767
if ( OnSameTeam( target, attacker ) ) {
779
set muzzle location relative to pivoting eye
782
void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
783
VectorCopy( ent->s.pos.trBase, muzzlePoint );
784
muzzlePoint[2] += ent->client->ps.viewheight;
785
VectorMA( muzzlePoint, 14, forward, muzzlePoint );
786
// snap to integer coordinates for more efficient network bandwidth usage
787
SnapVector( muzzlePoint );
792
CalcMuzzlePointOrigin
794
set muzzle location relative to pivoting eye
797
void CalcMuzzlePointOrigin ( gentity_t *ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
798
VectorCopy( ent->s.pos.trBase, muzzlePoint );
799
muzzlePoint[2] += ent->client->ps.viewheight;
800
VectorMA( muzzlePoint, 14, forward, muzzlePoint );
801
// snap to integer coordinates for more efficient network bandwidth usage
802
SnapVector( muzzlePoint );
812
void FireWeapon( gentity_t *ent ) {
813
if (ent->client->ps.powerups[PW_QUAD] ) {
814
s_quadFactor = g_quadfactor.value;
819
if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
824
// track shots taken for accuracy tracking. Grapple is not a weapon and gauntet is just not tracked
825
if( ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET ) {
827
if( ent->s.weapon == WP_NAILGUN ) {
828
ent->client->accuracy_shots += NUM_NAILSHOTS;
830
ent->client->accuracy_shots++;
833
ent->client->accuracy_shots++;
837
// set aiming directions
838
AngleVectors (ent->client->ps.viewangles, forward, right, up);
840
CalcMuzzlePointOrigin ( ent, ent->client->oldOrigin, forward, right, up, muzzle );
842
// fire the specific weapon
843
switch( ent->s.weapon ) {
845
Weapon_Gauntlet( ent );
848
Weapon_LightningFire( ent );
851
weapon_supershotgun_fire( ent );
854
if ( g_gametype.integer != GT_TEAM ) {
855
Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE );
857
Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE );
860
case WP_GRENADE_LAUNCHER:
861
weapon_grenadelauncher_fire( ent );
863
case WP_ROCKET_LAUNCHER:
864
Weapon_RocketLauncher_Fire( ent );
867
Weapon_Plasmagun_Fire( ent );
870
weapon_railgun_fire( ent );
875
case WP_GRAPPLING_HOOK:
876
Weapon_GrapplingHook_Fire( ent );
880
Weapon_Nailgun_Fire( ent );
882
case WP_PROX_LAUNCHER:
883
weapon_proxlauncher_fire( ent );
886
Bullet_Fire( ent, CHAINGUN_SPREAD, MACHINEGUN_DAMAGE );
890
// FIXME G_Error( "Bad ent->s.weapon" );
903
static void KamikazeRadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius ) {
906
int entityList[MAX_GENTITIES];
907
int numListedEntities;
917
for ( i = 0 ; i < 3 ; i++ ) {
918
mins[i] = origin[i] - radius;
919
maxs[i] = origin[i] + radius;
922
numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
924
for ( e = 0 ; e < numListedEntities ; e++ ) {
925
ent = &g_entities[entityList[ e ]];
927
if (!ent->takedamage) {
931
// dont hit things we have already hit
932
if( ent->kamikazeTime > level.time ) {
936
// find the distance from the edge of the bounding box
937
for ( i = 0 ; i < 3 ; i++ ) {
938
if ( origin[i] < ent->r.absmin[i] ) {
939
v[i] = ent->r.absmin[i] - origin[i];
940
} else if ( origin[i] > ent->r.absmax[i] ) {
941
v[i] = origin[i] - ent->r.absmax[i];
947
dist = VectorLength( v );
948
if ( dist >= radius ) {
952
// if( CanDamage (ent, origin) ) {
953
VectorSubtract (ent->r.currentOrigin, origin, dir);
954
// push the center of mass higher than the origin so players
955
// get knocked into the air more
957
G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
958
ent->kamikazeTime = level.time + 3000;
968
static void KamikazeShockWave( vec3_t origin, gentity_t *attacker, float damage, float push, float radius ) {
971
int entityList[MAX_GENTITIES];
972
int numListedEntities;
981
for ( i = 0 ; i < 3 ; i++ ) {
982
mins[i] = origin[i] - radius;
983
maxs[i] = origin[i] + radius;
986
numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
988
for ( e = 0 ; e < numListedEntities ; e++ ) {
989
ent = &g_entities[entityList[ e ]];
991
// dont hit things we have already hit
992
if( ent->kamikazeShockTime > level.time ) {
996
// find the distance from the edge of the bounding box
997
for ( i = 0 ; i < 3 ; i++ ) {
998
if ( origin[i] < ent->r.absmin[i] ) {
999
v[i] = ent->r.absmin[i] - origin[i];
1000
} else if ( origin[i] > ent->r.absmax[i] ) {
1001
v[i] = origin[i] - ent->r.absmax[i];
1007
dist = VectorLength( v );
1008
if ( dist >= radius ) {
1012
// if( CanDamage (ent, origin) ) {
1013
VectorSubtract (ent->r.currentOrigin, origin, dir);
1015
G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
1018
VectorNormalize(dir);
1019
if ( ent->client ) {
1020
ent->client->ps.velocity[0] = dir[0] * push;
1021
ent->client->ps.velocity[1] = dir[1] * push;
1022
ent->client->ps.velocity[2] = 100;
1024
ent->kamikazeShockTime = level.time + 3000;
1034
static void KamikazeDamage( gentity_t *self ) {
1042
if (self->count >= KAMI_SHOCKWAVE_STARTTIME) {
1043
// shockwave push back
1044
t = self->count - KAMI_SHOCKWAVE_STARTTIME;
1045
KamikazeShockWave(self->s.pos.trBase, self->activator, 25, 400, (int) (float) t * KAMI_SHOCKWAVE_MAXRADIUS / (KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME) );
1048
if (self->count >= KAMI_EXPLODE_STARTTIME) {
1050
t = self->count - KAMI_EXPLODE_STARTTIME;
1051
KamikazeRadiusDamage( self->s.pos.trBase, self->activator, 400, (int) (float) t * KAMI_BOOMSPHERE_MAXRADIUS / (KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME) );
1054
// either cycle or kill self
1055
if( self->count >= KAMI_SHOCKWAVE_ENDTIME ) {
1056
G_FreeEntity( self );
1059
self->nextthink = level.time + 100;
1061
// add earth quake effect
1062
newangles[0] = crandom() * 2;
1063
newangles[1] = crandom() * 2;
1065
for (i = 0; i < MAX_CLIENTS; i++)
1067
ent = &g_entities[i];
1073
if (ent->client->ps.groundEntityNum != ENTITYNUM_NONE) {
1074
ent->client->ps.velocity[0] += crandom() * 120;
1075
ent->client->ps.velocity[1] += crandom() * 120;
1076
ent->client->ps.velocity[2] = 30 + random() * 25;
1079
ent->client->ps.delta_angles[0] += ANGLE2SHORT(newangles[0] - self->movedir[0]);
1080
ent->client->ps.delta_angles[1] += ANGLE2SHORT(newangles[1] - self->movedir[1]);
1081
ent->client->ps.delta_angles[2] += ANGLE2SHORT(newangles[2] - self->movedir[2]);
1083
VectorCopy(newangles, self->movedir);
1091
void G_StartKamikaze( gentity_t *ent ) {
1092
gentity_t *explosion;
1096
// start up the explosion logic
1097
explosion = G_Spawn();
1099
explosion->s.eType = ET_EVENTS + EV_KAMIKAZE;
1100
explosion->eventTime = level.time;
1102
if ( ent->client ) {
1103
VectorCopy( ent->s.pos.trBase, snapped );
1106
VectorCopy( ent->activator->s.pos.trBase, snapped );
1108
SnapVector( snapped ); // save network bandwidth
1109
G_SetOrigin( explosion, snapped );
1111
explosion->classname = "kamikaze";
1112
explosion->s.pos.trType = TR_STATIONARY;
1114
explosion->kamikazeTime = level.time;
1116
explosion->think = KamikazeDamage;
1117
explosion->nextthink = level.time + 100;
1118
explosion->count = 0;
1119
VectorClear(explosion->movedir);
1121
trap_LinkEntity( explosion );
1125
explosion->activator = ent;
1127
ent->s.eFlags &= ~EF_KAMIKAZE;
1128
// nuke the guy that used it
1129
G_Damage( ent, ent, ent, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_KAMIKAZE );
1132
if ( !strcmp(ent->activator->classname, "bodyque") ) {
1133
explosion->activator = &g_entities[ent->activator->r.ownerNum];
1136
explosion->activator = ent->activator;
1140
// play global sound at all clients
1141
te = G_TempEntity(snapped, EV_GLOBAL_TEAM_SOUND );
1142
te->r.svFlags |= SVF_BROADCAST;
1143
te->s.eventParm = GTS_KAMIKAZE;