~ubuntu-branches/ubuntu/precise/openarena/precise

« back to all changes in this revision

Viewing changes to code/game/g_weapon.c

  • Committer: Bazaar Package Importer
  • Author(s): Bruno "Fuddl" Kleinert
  • Date: 2007-01-20 12:28:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070120122809-2yza5ojt7nqiyiam
Tags: upstream-0.6.0
ImportĀ upstreamĀ versionĀ 0.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
===========================================================================
 
3
Copyright (C) 1999-2005 Id Software, Inc.
 
4
 
 
5
This file is part of Quake III Arena source code.
 
6
 
 
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.
 
11
 
 
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.
 
16
 
 
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
===========================================================================
 
21
*/
 
22
//
 
23
// g_weapon.c 
 
24
// perform the server side effects of a weapon firing
 
25
 
 
26
#include "g_local.h"
 
27
 
 
28
static  float   s_quadFactor;
 
29
static  vec3_t  forward, right, up;
 
30
static  vec3_t  muzzle;
 
31
 
 
32
#define NUM_NAILSHOTS 15
 
33
 
 
34
/*
 
35
================
 
36
G_BounceProjectile
 
37
================
 
38
*/
 
39
void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) {
 
40
        vec3_t v, newv;
 
41
        float dot;
 
42
 
 
43
        VectorSubtract( impact, start, v );
 
44
        dot = DotProduct( v, dir );
 
45
        VectorMA( v, -2*dot, dir, newv );
 
46
 
 
47
        VectorNormalize(newv);
 
48
        VectorMA(impact, 8192, newv, endout);
 
49
}
 
50
 
 
51
 
 
52
/*
 
53
======================================================================
 
54
 
 
55
GAUNTLET
 
56
 
 
57
======================================================================
 
58
*/
 
59
 
 
60
void Weapon_Gauntlet( gentity_t *ent ) {
 
61
 
 
62
}
 
63
 
 
64
/*
 
65
===============
 
66
CheckGauntletAttack
 
67
===============
 
68
*/
 
69
qboolean CheckGauntletAttack( gentity_t *ent ) {
 
70
        trace_t         tr;
 
71
        vec3_t          end;
 
72
        gentity_t       *tent;
 
73
        gentity_t       *traceEnt;
 
74
        int                     damage;
 
75
 
 
76
        // set aiming directions
 
77
        AngleVectors (ent->client->ps.viewangles, forward, right, up);
 
78
 
 
79
        CalcMuzzlePoint ( ent, forward, right, up, muzzle );
 
80
 
 
81
        VectorMA (muzzle, 32, forward, end);
 
82
 
 
83
        trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
 
84
        if ( tr.surfaceFlags & SURF_NOIMPACT ) {
 
85
                return qfalse;
 
86
        }
 
87
 
 
88
        traceEnt = &g_entities[ tr.entityNum ];
 
89
 
 
90
        // send blood impact
 
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;
 
96
        }
 
97
 
 
98
        if ( !traceEnt->takedamage) {
 
99
                return qfalse;
 
100
        }
 
101
 
 
102
        if (ent->client->ps.powerups[PW_QUAD] ) {
 
103
                G_AddEvent( ent, EV_POWERUP_QUAD, 0 );
 
104
                s_quadFactor = g_quadfactor.value;
 
105
        } else {
 
106
                s_quadFactor = 1;
 
107
        }
 
108
#ifdef MISSIONPACK
 
109
        if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
 
110
                s_quadFactor *= 2;
 
111
        }
 
112
#endif
 
113
 
 
114
        damage = 50 * s_quadFactor;
 
115
        G_Damage( traceEnt, ent, ent, forward, tr.endpos,
 
116
                damage, 0, MOD_GAUNTLET );
 
117
 
 
118
        return qtrue;
 
119
}
 
120
 
 
121
 
 
122
/*
 
123
======================================================================
 
124
 
 
125
MACHINEGUN
 
126
 
 
127
======================================================================
 
128
*/
 
129
 
 
130
/*
 
131
======================
 
132
SnapVectorTowards
 
133
 
 
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 
 
137
into a wall.
 
138
======================
 
139
*/
 
140
void SnapVectorTowards( vec3_t v, vec3_t to ) {
 
141
        int             i;
 
142
 
 
143
        for ( i = 0 ; i < 3 ; i++ ) {
 
144
                if ( to[i] <= v[i] ) {
 
145
                        v[i] = (int)v[i];
 
146
                } else {
 
147
                        v[i] = (int)v[i] + 1;
 
148
                }
 
149
        }
 
150
}
 
151
 
 
152
#ifdef MISSIONPACK
 
153
#define CHAINGUN_SPREAD         600
 
154
#endif
 
155
#define MACHINEGUN_SPREAD       200
 
156
#define MACHINEGUN_DAMAGE       7
 
157
#define MACHINEGUN_TEAM_DAMAGE  5               // wimpier MG in teamplay
 
158
 
 
159
void Bullet_Fire (gentity_t *ent, float spread, int damage ) {
 
160
        trace_t         tr;
 
161
        vec3_t          end;
 
162
#ifdef MISSIONPACK
 
163
        vec3_t          impactpoint, bouncedir;
 
164
#endif
 
165
        float           r;
 
166
        float           u;
 
167
        gentity_t       *tent;
 
168
        gentity_t       *traceEnt;
 
169
        int                     i, passent;
 
170
 
 
171
        damage *= s_quadFactor;
 
172
 
 
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);
 
179
 
 
180
        passent = ent->s.number;
 
181
        for (i = 0; i < 10; i++) {
 
182
 
 
183
                trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);
 
184
                if ( tr.surfaceFlags & SURF_NOIMPACT ) {
 
185
                        return;
 
186
                }
 
187
 
 
188
                traceEnt = &g_entities[ tr.entityNum ];
 
189
 
 
190
                // snap the endpos to integers, but nudged towards the line
 
191
                SnapVectorTowards( tr.endpos, muzzle );
 
192
 
 
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++;
 
199
                        }
 
200
                } else {
 
201
                        tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL );
 
202
                        tent->s.eventParm = DirToByte( tr.plane.normal );
 
203
                }
 
204
                tent->s.otherEntityNum = ent->s.number;
 
205
 
 
206
                if ( traceEnt->takedamage) {
 
207
#ifdef MISSIONPACK
 
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;
 
214
                                }
 
215
                                else {
 
216
                                        VectorCopy( tr.endpos, muzzle );
 
217
                                        passent = traceEnt->s.number;
 
218
                                }
 
219
                                continue;
 
220
                        }
 
221
                        else {
 
222
#endif
 
223
                                G_Damage( traceEnt, ent, ent, forward, tr.endpos,
 
224
                                        damage, 0, MOD_MACHINEGUN);
 
225
#ifdef MISSIONPACK
 
226
                        }
 
227
#endif
 
228
                }
 
229
                break;
 
230
        }
 
231
}
 
232
 
 
233
 
 
234
/*
 
235
======================================================================
 
236
 
 
237
BFG
 
238
 
 
239
======================================================================
 
240
*/
 
241
 
 
242
void BFG_Fire ( gentity_t *ent ) {
 
243
        gentity_t       *m;
 
244
 
 
245
        m = fire_bfg (ent, muzzle, forward);
 
246
        m->damage *= s_quadFactor;
 
247
        m->splashDamage *= s_quadFactor;
 
248
 
 
249
//      VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );      // "real" physics
 
250
}
 
251
 
 
252
 
 
253
/*
 
254
======================================================================
 
255
 
 
256
SHOTGUN
 
257
 
 
258
======================================================================
 
259
*/
 
260
 
 
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
 
264
 
 
265
qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) {
 
266
        trace_t         tr;
 
267
        int                     damage, i, passent;
 
268
        gentity_t       *traceEnt;
 
269
#ifdef MISSIONPACK
 
270
        vec3_t          impactpoint, bouncedir;
 
271
#endif
 
272
        vec3_t          tr_start, tr_end;
 
273
 
 
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 ];
 
280
 
 
281
                // send bullet impact
 
282
                if (  tr.surfaceFlags & SURF_NOIMPACT ) {
 
283
                        return qfalse;
 
284
                }
 
285
 
 
286
                if ( traceEnt->takedamage) {
 
287
                        damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor;
 
288
#ifdef MISSIONPACK
 
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;
 
295
                                }
 
296
                                else {
 
297
                                        VectorCopy( tr.endpos, tr_start );
 
298
                                        passent = traceEnt->s.number;
 
299
                                }
 
300
                                continue;
 
301
                        }
 
302
                        else {
 
303
                                G_Damage( traceEnt, ent, ent, forward, tr.endpos,
 
304
                                        damage, 0, MOD_SHOTGUN);
 
305
                                if( LogAccuracyHit( traceEnt, ent ) ) {
 
306
                                        return qtrue;
 
307
                                }
 
308
                        }
 
309
#else
 
310
                        G_Damage( traceEnt, ent, ent, forward, tr.endpos,       damage, 0, MOD_SHOTGUN);
 
311
                                if( LogAccuracyHit( traceEnt, ent ) ) {
 
312
                                        return qtrue;
 
313
                                }
 
314
#endif
 
315
                }
 
316
                return qfalse;
 
317
        }
 
318
        return qfalse;
 
319
}
 
320
 
 
321
// this should match CG_ShotgunPattern
 
322
void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) {
 
323
        int                     i;
 
324
        float           r, u;
 
325
        vec3_t          end;
 
326
        vec3_t          forward, right, up;
 
327
        int                     oldScore;
 
328
        qboolean        hitClient = qfalse;
 
329
 
 
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 );
 
335
 
 
336
        oldScore = ent->client->ps.persistant[PERS_SCORE];
 
337
 
 
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 ) {
 
346
                        hitClient = qtrue;
 
347
                        ent->client->accuracy_hits++;
 
348
                }
 
349
        }
 
350
}
 
351
 
 
352
 
 
353
void weapon_supershotgun_fire (gentity_t *ent) {
 
354
        gentity_t               *tent;
 
355
 
 
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;
 
362
 
 
363
        ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
 
364
}
 
365
 
 
366
 
 
367
/*
 
368
======================================================================
 
369
 
 
370
GRENADE LAUNCHER
 
371
 
 
372
======================================================================
 
373
*/
 
374
 
 
375
void weapon_grenadelauncher_fire (gentity_t *ent) {
 
376
        gentity_t       *m;
 
377
 
 
378
        // extra vertical velocity
 
379
        forward[2] += 0.2f;
 
380
        VectorNormalize( forward );
 
381
 
 
382
        m = fire_grenade (ent, muzzle, forward);
 
383
        m->damage *= s_quadFactor;
 
384
        m->splashDamage *= s_quadFactor;
 
385
 
 
386
//      VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );      // "real" physics
 
387
}
 
388
 
 
389
/*
 
390
======================================================================
 
391
 
 
392
ROCKET
 
393
 
 
394
======================================================================
 
395
*/
 
396
 
 
397
void Weapon_RocketLauncher_Fire (gentity_t *ent) {
 
398
        gentity_t       *m;
 
399
 
 
400
        m = fire_rocket (ent, muzzle, forward);
 
401
        m->damage *= s_quadFactor;
 
402
        m->splashDamage *= s_quadFactor;
 
403
 
 
404
//      VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );      // "real" physics
 
405
}
 
406
 
 
407
 
 
408
/*
 
409
======================================================================
 
410
 
 
411
PLASMA GUN
 
412
 
 
413
======================================================================
 
414
*/
 
415
 
 
416
void Weapon_Plasmagun_Fire (gentity_t *ent) {
 
417
        gentity_t       *m;
 
418
 
 
419
        m = fire_plasma (ent, muzzle, forward);
 
420
        m->damage *= s_quadFactor;
 
421
        m->splashDamage *= s_quadFactor;
 
422
 
 
423
//      VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );      // "real" physics
 
424
}
 
425
 
 
426
/*
 
427
======================================================================
 
428
 
 
429
RAILGUN
 
430
 
 
431
======================================================================
 
432
*/
 
433
 
 
434
 
 
435
/*
 
436
=================
 
437
weapon_railgun_fire
 
438
=================
 
439
*/
 
440
#define MAX_RAIL_HITS   4
 
441
void weapon_railgun_fire (gentity_t *ent) {
 
442
        vec3_t          end;
 
443
#ifdef MISSIONPACK
 
444
        vec3_t impactpoint, bouncedir;
 
445
#endif
 
446
        trace_t         trace;
 
447
        gentity_t       *tent;
 
448
        gentity_t       *traceEnt;
 
449
        int                     damage;
 
450
        int                     i;
 
451
        int                     hits;
 
452
        int                     unlinked;
 
453
        int                     passent;
 
454
        gentity_t       *unlinkedEntities[MAX_RAIL_HITS];
 
455
 
 
456
        damage = 100 * s_quadFactor;
 
457
 
 
458
        VectorMA (muzzle, 8192, forward, end);
 
459
 
 
460
        // trace only against the solids, so the railgun will go through people
 
461
        unlinked = 0;
 
462
        hits = 0;
 
463
        passent = ent->s.number;
 
464
        do {
 
465
                trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT );
 
466
                if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) {
 
467
                        break;
 
468
                }
 
469
                traceEnt = &g_entities[ trace.entityNum ];
 
470
                if ( traceEnt->takedamage ) {
 
471
#ifdef MISSIONPACK
 
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
 
486
                                        //
 
487
                                        VectorCopy( impactpoint, muzzle );
 
488
                                        // the player can hit him/herself with the bounced rail
 
489
                                        passent = ENTITYNUM_NONE;
 
490
                                }
 
491
                        }
 
492
                        else {
 
493
                                if( LogAccuracyHit( traceEnt, ent ) ) {
 
494
                                        hits++;
 
495
                                }
 
496
                                G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
 
497
                        }
 
498
#else
 
499
                                if( LogAccuracyHit( traceEnt, ent ) ) {
 
500
                                        hits++;
 
501
                                }
 
502
                                G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
 
503
#endif
 
504
                }
 
505
                if ( trace.contents & CONTENTS_SOLID ) {
 
506
                        break;          // we hit something solid enough to stop the beam
 
507
                }
 
508
                // unlink this entity, so the next trace will go past it
 
509
                trap_UnlinkEntity( traceEnt );
 
510
                unlinkedEntities[unlinked] = traceEnt;
 
511
                unlinked++;
 
512
        } while ( unlinked < MAX_RAIL_HITS );
 
513
 
 
514
        // link back in any entities we unlinked
 
515
        for ( i = 0 ; i < unlinked ; i++ ) {
 
516
                trap_LinkEntity( unlinkedEntities[i] );
 
517
        }
 
518
 
 
519
        // the final trace endpos will be the terminal point of the rail trail
 
520
 
 
521
        // snap the endpos to integers to save net bandwidth, but nudged towards the line
 
522
        SnapVectorTowards( trace.endpos, muzzle );
 
523
 
 
524
        // send railgun beam effect
 
525
        tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );
 
526
 
 
527
        // set player number for custom colors on the railtrail
 
528
        tent->s.clientNum = ent->s.clientNum;
 
529
 
 
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 );
 
534
 
 
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
 
538
        } else {
 
539
                tent->s.eventParm = DirToByte( trace.plane.normal );
 
540
        }
 
541
        tent->s.clientNum = ent->s.clientNum;
 
542
 
 
543
        // give the shooter a reward sound if they have made two railgun hits in a row
 
544
        if ( hits == 0 ) {
 
545
                // complete miss
 
546
                ent->client->accurateCount = 0;
 
547
        } else {
 
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;
 
557
                }
 
558
                ent->client->accuracy_hits++;
 
559
        }
 
560
 
 
561
}
 
562
 
 
563
 
 
564
/*
 
565
======================================================================
 
566
 
 
567
GRAPPLING HOOK
 
568
 
 
569
======================================================================
 
570
*/
 
571
 
 
572
void Weapon_GrapplingHook_Fire (gentity_t *ent)
 
573
{
 
574
        if (!ent->client->fireHeld && !ent->client->hook)
 
575
                fire_grapple (ent, muzzle, forward);
 
576
 
 
577
        ent->client->fireHeld = qtrue;
 
578
}
 
579
 
 
580
void Weapon_HookFree (gentity_t *ent)
 
581
{
 
582
        ent->parent->client->hook = NULL;
 
583
        ent->parent->client->ps.pm_flags &= ~PMF_GRAPPLE_PULL;
 
584
        G_FreeEntity( ent );
 
585
}
 
586
 
 
587
void Weapon_HookThink (gentity_t *ent)
 
588
{
 
589
        if (ent->enemy) {
 
590
                vec3_t v, oldorigin;
 
591
 
 
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
 
597
 
 
598
                G_SetOrigin( ent, v );
 
599
        }
 
600
 
 
601
        VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);
 
602
}
 
603
 
 
604
/*
 
605
======================================================================
 
606
 
 
607
LIGHTNING GUN
 
608
 
 
609
======================================================================
 
610
*/
 
611
 
 
612
void Weapon_LightningFire( gentity_t *ent ) {
 
613
        trace_t         tr;
 
614
        vec3_t          end;
 
615
#ifdef MISSIONPACK
 
616
        vec3_t impactpoint, bouncedir;
 
617
#endif
 
618
        gentity_t       *traceEnt, *tent;
 
619
        int                     damage, i, passent;
 
620
 
 
621
        damage = 8 * s_quadFactor;
 
622
 
 
623
        passent = ent->s.number;
 
624
        for (i = 0; i < 10; i++) {
 
625
                VectorMA( muzzle, LIGHTNING_RANGE, forward, end );
 
626
 
 
627
                trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT );
 
628
 
 
629
#ifdef MISSIONPACK
 
630
                // if not the first trace (the lightning bounced of an invulnerability sphere)
 
631
                if (i) {
 
632
                        // add bounced off lightning bolt temp entity
 
633
                        // the first lightning bolt is a cgame only visual
 
634
                        //
 
635
                        tent = G_TempEntity( muzzle, EV_LIGHTNINGBOLT );
 
636
                        VectorCopy( tr.endpos, end );
 
637
                        SnapVector( end );
 
638
                        VectorCopy( end, tent->s.origin2 );
 
639
                }
 
640
#endif
 
641
                if ( tr.entityNum == ENTITYNUM_NONE ) {
 
642
                        return;
 
643
                }
 
644
 
 
645
                traceEnt = &g_entities[ tr.entityNum ];
 
646
 
 
647
                if ( traceEnt->takedamage) {
 
648
#ifdef MISSIONPACK
 
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;
 
657
                                }
 
658
                                else {
 
659
                                        VectorCopy( tr.endpos, muzzle );
 
660
                                        passent = traceEnt->s.number;
 
661
                                }
 
662
                                continue;
 
663
                        }
 
664
                        else {
 
665
                                G_Damage( traceEnt, ent, ent, forward, tr.endpos,
 
666
                                        damage, 0, MOD_LIGHTNING);
 
667
                        }
 
668
#else
 
669
                                G_Damage( traceEnt, ent, ent, forward, tr.endpos,
 
670
                                        damage, 0, MOD_LIGHTNING);
 
671
#endif
 
672
                }
 
673
 
 
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++;
 
681
                        }
 
682
                } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
 
683
                        tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
 
684
                        tent->s.eventParm = DirToByte( tr.plane.normal );
 
685
                }
 
686
 
 
687
                break;
 
688
        }
 
689
}
 
690
 
 
691
#ifdef MISSIONPACK
 
692
/*
 
693
======================================================================
 
694
 
 
695
NAILGUN
 
696
 
 
697
======================================================================
 
698
*/
 
699
 
 
700
void Weapon_Nailgun_Fire (gentity_t *ent) {
 
701
        gentity_t       *m;
 
702
        int                     count;
 
703
 
 
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;
 
708
        }
 
709
 
 
710
//      VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );      // "real" physics
 
711
}
 
712
 
 
713
 
 
714
/*
 
715
======================================================================
 
716
 
 
717
PROXIMITY MINE LAUNCHER
 
718
 
 
719
======================================================================
 
720
*/
 
721
 
 
722
void weapon_proxlauncher_fire (gentity_t *ent) {
 
723
        gentity_t       *m;
 
724
 
 
725
        // extra vertical velocity
 
726
        forward[2] += 0.2f;
 
727
        VectorNormalize( forward );
 
728
 
 
729
        m = fire_prox (ent, muzzle, forward);
 
730
        m->damage *= s_quadFactor;
 
731
        m->splashDamage *= s_quadFactor;
 
732
 
 
733
//      VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );      // "real" physics
 
734
}
 
735
 
 
736
#endif
 
737
 
 
738
//======================================================================
 
739
 
 
740
 
 
741
/*
 
742
===============
 
743
LogAccuracyHit
 
744
===============
 
745
*/
 
746
qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
 
747
        if( !target->takedamage ) {
 
748
                return qfalse;
 
749
        }
 
750
 
 
751
        if ( target == attacker ) {
 
752
                return qfalse;
 
753
        }
 
754
 
 
755
        if( !target->client ) {
 
756
                return qfalse;
 
757
        }
 
758
 
 
759
        if( !attacker->client ) {
 
760
                return qfalse;
 
761
        }
 
762
 
 
763
        if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
 
764
                return qfalse;
 
765
        }
 
766
 
 
767
        if ( OnSameTeam( target, attacker ) ) {
 
768
                return qfalse;
 
769
        }
 
770
 
 
771
        return qtrue;
 
772
}
 
773
 
 
774
 
 
775
/*
 
776
===============
 
777
CalcMuzzlePoint
 
778
 
 
779
set muzzle location relative to pivoting eye
 
780
===============
 
781
*/
 
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 );
 
788
}
 
789
 
 
790
/*
 
791
===============
 
792
CalcMuzzlePointOrigin
 
793
 
 
794
set muzzle location relative to pivoting eye
 
795
===============
 
796
*/
 
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 );
 
803
}
 
804
 
 
805
 
 
806
 
 
807
/*
 
808
===============
 
809
FireWeapon
 
810
===============
 
811
*/
 
812
void FireWeapon( gentity_t *ent ) {
 
813
        if (ent->client->ps.powerups[PW_QUAD] ) {
 
814
                s_quadFactor = g_quadfactor.value;
 
815
        } else {
 
816
                s_quadFactor = 1;
 
817
        }
 
818
#ifdef MISSIONPACK
 
819
        if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
 
820
                s_quadFactor *= 2;
 
821
        }
 
822
#endif
 
823
 
 
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 ) {
 
826
#ifdef MISSIONPACK
 
827
                if( ent->s.weapon == WP_NAILGUN ) {
 
828
                        ent->client->accuracy_shots += NUM_NAILSHOTS;
 
829
                } else {
 
830
                        ent->client->accuracy_shots++;
 
831
                }
 
832
#else
 
833
                ent->client->accuracy_shots++;
 
834
#endif
 
835
        }
 
836
 
 
837
        // set aiming directions
 
838
        AngleVectors (ent->client->ps.viewangles, forward, right, up);
 
839
 
 
840
        CalcMuzzlePointOrigin ( ent, ent->client->oldOrigin, forward, right, up, muzzle );
 
841
 
 
842
        // fire the specific weapon
 
843
        switch( ent->s.weapon ) {
 
844
        case WP_GAUNTLET:
 
845
                Weapon_Gauntlet( ent );
 
846
                break;
 
847
        case WP_LIGHTNING:
 
848
                Weapon_LightningFire( ent );
 
849
                break;
 
850
        case WP_SHOTGUN:
 
851
                weapon_supershotgun_fire( ent );
 
852
                break;
 
853
        case WP_MACHINEGUN:
 
854
                if ( g_gametype.integer != GT_TEAM ) {
 
855
                        Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE );
 
856
                } else {
 
857
                        Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE );
 
858
                }
 
859
                break;
 
860
        case WP_GRENADE_LAUNCHER:
 
861
                weapon_grenadelauncher_fire( ent );
 
862
                break;
 
863
        case WP_ROCKET_LAUNCHER:
 
864
                Weapon_RocketLauncher_Fire( ent );
 
865
                break;
 
866
        case WP_PLASMAGUN:
 
867
                Weapon_Plasmagun_Fire( ent );
 
868
                break;
 
869
        case WP_RAILGUN:
 
870
                weapon_railgun_fire( ent );
 
871
                break;
 
872
        case WP_BFG:
 
873
                BFG_Fire( ent );
 
874
                break;
 
875
        case WP_GRAPPLING_HOOK:
 
876
                Weapon_GrapplingHook_Fire( ent );
 
877
                break;
 
878
#ifdef MISSIONPACK
 
879
        case WP_NAILGUN:
 
880
                Weapon_Nailgun_Fire( ent );
 
881
                break;
 
882
        case WP_PROX_LAUNCHER:
 
883
                weapon_proxlauncher_fire( ent );
 
884
                break;
 
885
        case WP_CHAINGUN:
 
886
                Bullet_Fire( ent, CHAINGUN_SPREAD, MACHINEGUN_DAMAGE );
 
887
                break;
 
888
#endif
 
889
        default:
 
890
// FIXME                G_Error( "Bad ent->s.weapon" );
 
891
                break;
 
892
        }
 
893
}
 
894
 
 
895
 
 
896
#ifdef MISSIONPACK
 
897
 
 
898
/*
 
899
===============
 
900
KamikazeRadiusDamage
 
901
===============
 
902
*/
 
903
static void KamikazeRadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius ) {
 
904
        float           dist;
 
905
        gentity_t       *ent;
 
906
        int                     entityList[MAX_GENTITIES];
 
907
        int                     numListedEntities;
 
908
        vec3_t          mins, maxs;
 
909
        vec3_t          v;
 
910
        vec3_t          dir;
 
911
        int                     i, e;
 
912
 
 
913
        if ( radius < 1 ) {
 
914
                radius = 1;
 
915
        }
 
916
 
 
917
        for ( i = 0 ; i < 3 ; i++ ) {
 
918
                mins[i] = origin[i] - radius;
 
919
                maxs[i] = origin[i] + radius;
 
920
        }
 
921
 
 
922
        numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
 
923
 
 
924
        for ( e = 0 ; e < numListedEntities ; e++ ) {
 
925
                ent = &g_entities[entityList[ e ]];
 
926
 
 
927
                if (!ent->takedamage) {
 
928
                        continue;
 
929
                }
 
930
 
 
931
                // dont hit things we have already hit
 
932
                if( ent->kamikazeTime > level.time ) {
 
933
                        continue;
 
934
                }
 
935
 
 
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];
 
942
                        } else {
 
943
                                v[i] = 0;
 
944
                        }
 
945
                }
 
946
 
 
947
                dist = VectorLength( v );
 
948
                if ( dist >= radius ) {
 
949
                        continue;
 
950
                }
 
951
 
 
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
 
956
                        dir[2] += 24;
 
957
                        G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
 
958
                        ent->kamikazeTime = level.time + 3000;
 
959
//              }
 
960
        }
 
961
}
 
962
 
 
963
/*
 
964
===============
 
965
KamikazeShockWave
 
966
===============
 
967
*/
 
968
static void KamikazeShockWave( vec3_t origin, gentity_t *attacker, float damage, float push, float radius ) {
 
969
        float           dist;
 
970
        gentity_t       *ent;
 
971
        int                     entityList[MAX_GENTITIES];
 
972
        int                     numListedEntities;
 
973
        vec3_t          mins, maxs;
 
974
        vec3_t          v;
 
975
        vec3_t          dir;
 
976
        int                     i, e;
 
977
 
 
978
        if ( radius < 1 )
 
979
                radius = 1;
 
980
 
 
981
        for ( i = 0 ; i < 3 ; i++ ) {
 
982
                mins[i] = origin[i] - radius;
 
983
                maxs[i] = origin[i] + radius;
 
984
        }
 
985
 
 
986
        numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
 
987
 
 
988
        for ( e = 0 ; e < numListedEntities ; e++ ) {
 
989
                ent = &g_entities[entityList[ e ]];
 
990
 
 
991
                // dont hit things we have already hit
 
992
                if( ent->kamikazeShockTime > level.time ) {
 
993
                        continue;
 
994
                }
 
995
 
 
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];
 
1002
                        } else {
 
1003
                                v[i] = 0;
 
1004
                        }
 
1005
                }
 
1006
 
 
1007
                dist = VectorLength( v );
 
1008
                if ( dist >= radius ) {
 
1009
                        continue;
 
1010
                }
 
1011
 
 
1012
//              if( CanDamage (ent, origin) ) {
 
1013
                        VectorSubtract (ent->r.currentOrigin, origin, dir);
 
1014
                        dir[2] += 24;
 
1015
                        G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
 
1016
                        //
 
1017
                        dir[2] = 0;
 
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;
 
1023
                        }
 
1024
                        ent->kamikazeShockTime = level.time + 3000;
 
1025
//              }
 
1026
        }
 
1027
}
 
1028
 
 
1029
/*
 
1030
===============
 
1031
KamikazeDamage
 
1032
===============
 
1033
*/
 
1034
static void KamikazeDamage( gentity_t *self ) {
 
1035
        int i;
 
1036
        float t;
 
1037
        gentity_t *ent;
 
1038
        vec3_t newangles;
 
1039
 
 
1040
        self->count += 100;
 
1041
 
 
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) );
 
1046
        }
 
1047
        //
 
1048
        if (self->count >= KAMI_EXPLODE_STARTTIME) {
 
1049
                // do our damage
 
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) );
 
1052
        }
 
1053
 
 
1054
        // either cycle or kill self
 
1055
        if( self->count >= KAMI_SHOCKWAVE_ENDTIME ) {
 
1056
                G_FreeEntity( self );
 
1057
                return;
 
1058
        }
 
1059
        self->nextthink = level.time + 100;
 
1060
 
 
1061
        // add earth quake effect
 
1062
        newangles[0] = crandom() * 2;
 
1063
        newangles[1] = crandom() * 2;
 
1064
        newangles[2] = 0;
 
1065
        for (i = 0; i < MAX_CLIENTS; i++)
 
1066
        {
 
1067
                ent = &g_entities[i];
 
1068
                if (!ent->inuse)
 
1069
                        continue;
 
1070
                if (!ent->client)
 
1071
                        continue;
 
1072
 
 
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;
 
1077
                }
 
1078
 
 
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]);
 
1082
        }
 
1083
        VectorCopy(newangles, self->movedir);
 
1084
}
 
1085
 
 
1086
/*
 
1087
===============
 
1088
G_StartKamikaze
 
1089
===============
 
1090
*/
 
1091
void G_StartKamikaze( gentity_t *ent ) {
 
1092
        gentity_t       *explosion;
 
1093
        gentity_t       *te;
 
1094
        vec3_t          snapped;
 
1095
 
 
1096
        // start up the explosion logic
 
1097
        explosion = G_Spawn();
 
1098
 
 
1099
        explosion->s.eType = ET_EVENTS + EV_KAMIKAZE;
 
1100
        explosion->eventTime = level.time;
 
1101
 
 
1102
        if ( ent->client ) {
 
1103
                VectorCopy( ent->s.pos.trBase, snapped );
 
1104
        }
 
1105
        else {
 
1106
                VectorCopy( ent->activator->s.pos.trBase, snapped );
 
1107
        }
 
1108
        SnapVector( snapped );          // save network bandwidth
 
1109
        G_SetOrigin( explosion, snapped );
 
1110
 
 
1111
        explosion->classname = "kamikaze";
 
1112
        explosion->s.pos.trType = TR_STATIONARY;
 
1113
 
 
1114
        explosion->kamikazeTime = level.time;
 
1115
 
 
1116
        explosion->think = KamikazeDamage;
 
1117
        explosion->nextthink = level.time + 100;
 
1118
        explosion->count = 0;
 
1119
        VectorClear(explosion->movedir);
 
1120
 
 
1121
        trap_LinkEntity( explosion );
 
1122
 
 
1123
        if (ent->client) {
 
1124
                //
 
1125
                explosion->activator = ent;
 
1126
                //
 
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 );
 
1130
        }
 
1131
        else {
 
1132
                if ( !strcmp(ent->activator->classname, "bodyque") ) {
 
1133
                        explosion->activator = &g_entities[ent->activator->r.ownerNum];
 
1134
                }
 
1135
                else {
 
1136
                        explosion->activator = ent->activator;
 
1137
                }
 
1138
        }
 
1139
 
 
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;
 
1144
}
 
1145
#endif