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 #include "g_local.h"
24
25 #define MISSILE_PRESTEP_TIME 50
26
27 /*
28 ================
29 G_BounceMissile
30
31 ================
32 */
G_BounceMissile(gentity_t * ent,trace_t * trace)33 void G_BounceMissile( gentity_t *ent, trace_t *trace ) {
34 vec3_t velocity;
35 float dot;
36 int hitTime;
37
38 // reflect the velocity on the trace plane
39 hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
40 BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
41 dot = DotProduct( velocity, trace->plane.normal );
42 VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
43
44 if ( ent->s.eFlags & EF_BOUNCE_HALF ) {
45 VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta );
46 // check for stop
47 if ( trace->plane.normal[2] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 ) {
48 G_SetOrigin( ent, trace->endpos );
49 return;
50 }
51 }
52
53 VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
54 VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
55 ent->s.pos.trTime = level.time;
56 }
57
58
59 /*
60 ================
61 G_ExplodeMissile
62
63 Explode a missile without an impact
64 ================
65 */
G_ExplodeMissile(gentity_t * ent)66 void G_ExplodeMissile( gentity_t *ent ) {
67 vec3_t dir;
68 vec3_t origin;
69
70 BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
71 SnapVector( origin );
72 G_SetOrigin( ent, origin );
73
74 // we don't have a valid direction, so just point straight up
75 dir[0] = dir[1] = 0;
76 dir[2] = 1;
77
78 ent->s.eType = ET_GENERAL;
79 G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) );
80
81 ent->freeAfterEvent = qtrue;
82
83 // splash damage
84 if ( ent->splashDamage ) {
85 if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent
86 , ent->splashMethodOfDeath ) ) {
87 g_entities[ent->r.ownerNum].client->accuracy_hits++;
88 }
89 }
90
91 trap_LinkEntity( ent );
92 }
93
94
95 #ifdef MISSIONPACK
96 /*
97 ================
98 ProximityMine_Explode
99 ================
100 */
ProximityMine_Explode(gentity_t * mine)101 static void ProximityMine_Explode( gentity_t *mine ) {
102 G_ExplodeMissile( mine );
103 // if the prox mine has a trigger free it
104 if (mine->activator) {
105 G_FreeEntity(mine->activator);
106 mine->activator = NULL;
107 }
108 }
109
110 /*
111 ================
112 ProximityMine_Die
113 ================
114 */
ProximityMine_Die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)115 static void ProximityMine_Die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
116 ent->think = ProximityMine_Explode;
117 ent->nextthink = level.time + 1;
118 }
119
120 /*
121 ================
122 ProximityMine_Trigger
123 ================
124 */
ProximityMine_Trigger(gentity_t * trigger,gentity_t * other,trace_t * trace)125 void ProximityMine_Trigger( gentity_t *trigger, gentity_t *other, trace_t *trace ) {
126 vec3_t v;
127 gentity_t *mine;
128
129 if( !other->client ) {
130 return;
131 }
132
133 // trigger is a cube, do a distance test now to act as if it's a sphere
134 VectorSubtract( trigger->s.pos.trBase, other->s.pos.trBase, v );
135 if( VectorLength( v ) > trigger->parent->splashRadius ) {
136 return;
137 }
138
139
140 if ( g_gametype.integer >= GT_TEAM ) {
141 // don't trigger same team mines
142 if (trigger->parent->s.generic1 == other->client->sess.sessionTeam) {
143 return;
144 }
145 }
146
147 // ok, now check for ability to damage so we don't get triggered thru walls, closed doors, etc...
148 if( !CanDamage( other, trigger->s.pos.trBase ) ) {
149 return;
150 }
151
152 // trigger the mine!
153 mine = trigger->parent;
154 mine->s.loopSound = 0;
155 G_AddEvent( mine, EV_PROXIMITY_MINE_TRIGGER, 0 );
156 mine->nextthink = level.time + 500;
157
158 G_FreeEntity( trigger );
159 }
160
161 /*
162 ================
163 ProximityMine_Activate
164 ================
165 */
ProximityMine_Activate(gentity_t * ent)166 static void ProximityMine_Activate( gentity_t *ent ) {
167 gentity_t *trigger;
168 float r;
169
170 ent->think = ProximityMine_Explode;
171 ent->nextthink = level.time + g_proxMineTimeout.integer;
172
173 ent->takedamage = qtrue;
174 ent->health = 1;
175 ent->die = ProximityMine_Die;
176
177 ent->s.loopSound = G_SoundIndex( "sound/weapons/proxmine/wstbtick.wav" );
178
179 // build the proximity trigger
180 trigger = G_Spawn ();
181
182 trigger->classname = "proxmine_trigger";
183
184 r = ent->splashRadius;
185 VectorSet( trigger->r.mins, -r, -r, -r );
186 VectorSet( trigger->r.maxs, r, r, r );
187
188 G_SetOrigin( trigger, ent->s.pos.trBase );
189
190 trigger->parent = ent;
191 trigger->r.contents = CONTENTS_TRIGGER;
192 trigger->touch = ProximityMine_Trigger;
193
194 trap_LinkEntity (trigger);
195
196 // set pointer to trigger so the entity can be freed when the mine explodes
197 ent->activator = trigger;
198 }
199
200 /*
201 ================
202 ProximityMine_ExplodeOnPlayer
203 ================
204 */
ProximityMine_ExplodeOnPlayer(gentity_t * mine)205 static void ProximityMine_ExplodeOnPlayer( gentity_t *mine ) {
206 gentity_t *player;
207
208 player = mine->enemy;
209 player->client->ps.eFlags &= ~EF_TICKING;
210
211 if ( player->client->invulnerabilityTime > level.time ) {
212 G_Damage( player, mine->parent, mine->parent, vec3_origin, mine->s.origin, 1000, DAMAGE_NO_KNOCKBACK, MOD_JUICED );
213 player->client->invulnerabilityTime = 0;
214 G_TempEntity( player->client->ps.origin, EV_JUICED );
215 }
216 else {
217 G_SetOrigin( mine, player->s.pos.trBase );
218 // make sure the explosion gets to the client
219 mine->r.svFlags &= ~SVF_NOCLIENT;
220 mine->splashMethodOfDeath = MOD_PROXIMITY_MINE;
221 G_ExplodeMissile( mine );
222 }
223 }
224
225 /*
226 ================
227 ProximityMine_Player
228 ================
229 */
ProximityMine_Player(gentity_t * mine,gentity_t * player)230 static void ProximityMine_Player( gentity_t *mine, gentity_t *player ) {
231 if( mine->s.eFlags & EF_NODRAW ) {
232 return;
233 }
234
235 G_AddEvent( mine, EV_PROXIMITY_MINE_STICK, 0 );
236
237 if( player->s.eFlags & EF_TICKING ) {
238 player->activator->splashDamage += mine->splashDamage;
239 player->activator->splashRadius *= 1.50;
240 mine->think = G_FreeEntity;
241 mine->nextthink = level.time;
242 return;
243 }
244
245 player->client->ps.eFlags |= EF_TICKING;
246 player->activator = mine;
247
248 mine->s.eFlags |= EF_NODRAW;
249 mine->r.svFlags |= SVF_NOCLIENT;
250 mine->s.pos.trType = TR_LINEAR;
251 VectorClear( mine->s.pos.trDelta );
252
253 mine->enemy = player;
254 mine->think = ProximityMine_ExplodeOnPlayer;
255 if ( player->client->invulnerabilityTime > level.time ) {
256 mine->nextthink = level.time + 2 * 1000;
257 }
258 else {
259 mine->nextthink = level.time + 10 * 1000;
260 }
261 }
262 #endif
263
264 /*
265 ================
266 G_MissileImpact
267 ================
268 */
G_MissileImpact(gentity_t * ent,trace_t * trace)269 void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
270 gentity_t *other;
271 qboolean hitClient = qfalse;
272 #ifdef MISSIONPACK
273 vec3_t forward, impactpoint, bouncedir;
274 int eFlags;
275 #endif
276 other = &g_entities[trace->entityNum];
277
278 // check for bounce
279 if ( !other->takedamage &&
280 ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
281 G_BounceMissile( ent, trace );
282 G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
283 return;
284 }
285
286 #ifdef MISSIONPACK
287 if ( other->takedamage ) {
288 if ( ent->s.weapon != WP_PROX_LAUNCHER ) {
289 if ( other->client && other->client->invulnerabilityTime > level.time ) {
290 //
291 VectorCopy( ent->s.pos.trDelta, forward );
292 VectorNormalize( forward );
293 if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) {
294 VectorCopy( bouncedir, trace->plane.normal );
295 eFlags = ent->s.eFlags & EF_BOUNCE_HALF;
296 ent->s.eFlags &= ~EF_BOUNCE_HALF;
297 G_BounceMissile( ent, trace );
298 ent->s.eFlags |= eFlags;
299 }
300 ent->target_ent = other;
301 return;
302 }
303 }
304 }
305 #endif
306 // impact damage
307 if (other->takedamage) {
308 // FIXME: wrong damage direction?
309 if ( ent->damage ) {
310 vec3_t velocity;
311
312 if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) {
313 g_entities[ent->r.ownerNum].client->accuracy_hits++;
314 hitClient = qtrue;
315 }
316 BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
317 if ( VectorLength( velocity ) == 0 ) {
318 velocity[2] = 1; // stepped on a grenade
319 }
320 G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
321 ent->s.origin, ent->damage,
322 0, ent->methodOfDeath);
323 }
324 }
325
326 #ifdef MISSIONPACK
327 if( ent->s.weapon == WP_PROX_LAUNCHER ) {
328 if( ent->s.pos.trType != TR_GRAVITY ) {
329 return;
330 }
331
332 // if it's a player, stick it on to them (flag them and remove this entity)
333 if( other->s.eType == ET_PLAYER && other->health > 0 ) {
334 ProximityMine_Player( ent, other );
335 return;
336 }
337
338 SnapVectorTowards( trace->endpos, ent->s.pos.trBase );
339 G_SetOrigin( ent, trace->endpos );
340 ent->s.pos.trType = TR_STATIONARY;
341 VectorClear( ent->s.pos.trDelta );
342
343 G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags );
344
345 ent->think = ProximityMine_Activate;
346 ent->nextthink = level.time + 2000;
347
348 vectoangles( trace->plane.normal, ent->s.angles );
349 ent->s.angles[0] += 90;
350
351 // link the prox mine to the other entity
352 ent->enemy = other;
353 ent->die = ProximityMine_Die;
354 VectorCopy(trace->plane.normal, ent->movedir);
355 VectorSet(ent->r.mins, -4, -4, -4);
356 VectorSet(ent->r.maxs, 4, 4, 4);
357 trap_LinkEntity(ent);
358
359 return;
360 }
361 #endif
362
363 if (!strcmp(ent->classname, "hook")) {
364 gentity_t *nent;
365 vec3_t v;
366
367 nent = G_Spawn();
368 if ( other->takedamage && other->client ) {
369
370 G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
371 nent->s.otherEntityNum = other->s.number;
372
373 ent->enemy = other;
374
375 v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5;
376 v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5;
377 v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5;
378
379 SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth
380 } else {
381 VectorCopy(trace->endpos, v);
382 G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
383 ent->enemy = NULL;
384 }
385
386 SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth
387
388 nent->freeAfterEvent = qtrue;
389 // change over to a normal entity right at the point of impact
390 nent->s.eType = ET_GENERAL;
391 ent->s.eType = ET_GRAPPLE;
392
393 G_SetOrigin( ent, v );
394 G_SetOrigin( nent, v );
395
396 ent->think = Weapon_HookThink;
397 ent->nextthink = level.time + FRAMETIME;
398
399 ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL;
400 VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);
401
402 trap_LinkEntity( ent );
403 trap_LinkEntity( nent );
404
405 return;
406 }
407
408 // is it cheaper in bandwidth to just remove this ent and create a new
409 // one, rather than changing the missile into the explosion?
410
411 if ( other->takedamage && other->client ) {
412 G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
413 ent->s.otherEntityNum = other->s.number;
414 } else if( trace->surfaceFlags & SURF_METALSTEPS ) {
415 G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) );
416 } else {
417 G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
418 }
419
420 ent->freeAfterEvent = qtrue;
421
422 // change over to a normal entity right at the point of impact
423 ent->s.eType = ET_GENERAL;
424
425 SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth
426
427 G_SetOrigin( ent, trace->endpos );
428
429 // splash damage (doesn't apply to person directly hit)
430 if ( ent->splashDamage ) {
431 if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
432 other, ent->splashMethodOfDeath ) ) {
433 if( !hitClient ) {
434 g_entities[ent->r.ownerNum].client->accuracy_hits++;
435 }
436 }
437 }
438
439 trap_LinkEntity( ent );
440 }
441
442 /*
443 ================
444 G_RunMissile
445 ================
446 */
G_RunMissile(gentity_t * ent)447 void G_RunMissile( gentity_t *ent ) {
448 vec3_t origin;
449 trace_t tr;
450 int passent;
451
452 // get current position
453 BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
454
455 // if this missile bounced off an invulnerability sphere
456 if ( ent->target_ent ) {
457 passent = ent->target_ent->s.number;
458 }
459 #ifdef MISSIONPACK
460 // prox mines that left the owner bbox will attach to anything, even the owner
461 else if (ent->s.weapon == WP_PROX_LAUNCHER && ent->count) {
462 passent = ENTITYNUM_NONE;
463 }
464 #endif
465 else {
466 // ignore interactions with the missile owner
467 passent = ent->r.ownerNum;
468 }
469 // trace a line from the previous position to the current position
470 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask );
471
472 if ( tr.startsolid || tr.allsolid ) {
473 // make sure the tr.entityNum is set to the entity we're stuck in
474 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask );
475 tr.fraction = 0;
476 }
477 else {
478 VectorCopy( tr.endpos, ent->r.currentOrigin );
479 }
480
481 trap_LinkEntity( ent );
482
483 if ( tr.fraction != 1 ) {
484 // never explode or bounce on sky
485 if ( tr.surfaceFlags & SURF_NOIMPACT ) {
486 // If grapple, reset owner
487 if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) {
488 ent->parent->client->hook = NULL;
489 }
490 G_FreeEntity( ent );
491 return;
492 }
493 G_MissileImpact( ent, &tr );
494 if ( ent->s.eType != ET_MISSILE ) {
495 return; // exploded
496 }
497 }
498 #ifdef MISSIONPACK
499 // if the prox mine wasn't yet outside the player body
500 if (ent->s.weapon == WP_PROX_LAUNCHER && !ent->count) {
501 // check if the prox mine is outside the owner bbox
502 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ENTITYNUM_NONE, ent->clipmask );
503 if (!tr.startsolid || tr.entityNum != ent->r.ownerNum) {
504 ent->count = 1;
505 }
506 }
507 #endif
508 // check think function after bouncing
509 G_RunThink( ent );
510 }
511
512
513 //=============================================================================
514
515 /*
516 =================
517 fire_plasma
518
519 =================
520 */
fire_plasma(gentity_t * self,vec3_t start,vec3_t dir)521 gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t dir) {
522 gentity_t *bolt;
523
524 VectorNormalize (dir);
525
526 bolt = G_Spawn();
527 bolt->classname = "plasma";
528 bolt->nextthink = level.time + 10000;
529 bolt->think = G_ExplodeMissile;
530 bolt->s.eType = ET_MISSILE;
531 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
532 bolt->s.weapon = WP_PLASMAGUN;
533 bolt->r.ownerNum = self->s.number;
534 bolt->parent = self;
535 bolt->damage = 20;
536 bolt->splashDamage = 15;
537 bolt->splashRadius = 20;
538 bolt->methodOfDeath = MOD_PLASMA;
539 bolt->splashMethodOfDeath = MOD_PLASMA_SPLASH;
540 bolt->clipmask = MASK_SHOT;
541 bolt->target_ent = NULL;
542
543 bolt->s.pos.trType = TR_LINEAR;
544 bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
545 VectorCopy( start, bolt->s.pos.trBase );
546 VectorScale( dir, 2000, bolt->s.pos.trDelta );
547 SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
548
549 VectorCopy (start, bolt->r.currentOrigin);
550
551 return bolt;
552 }
553
554 //=============================================================================
555
556
557 /*
558 =================
559 fire_grenade
560 =================
561 */
fire_grenade(gentity_t * self,vec3_t start,vec3_t dir)562 gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t dir) {
563 gentity_t *bolt;
564
565 VectorNormalize (dir);
566
567 bolt = G_Spawn();
568 bolt->classname = "grenade";
569 bolt->nextthink = level.time + 2500;
570 bolt->think = G_ExplodeMissile;
571 bolt->s.eType = ET_MISSILE;
572 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
573 bolt->s.weapon = WP_GRENADE_LAUNCHER;
574 bolt->s.eFlags = EF_BOUNCE_HALF;
575 bolt->r.ownerNum = self->s.number;
576 bolt->parent = self;
577 bolt->damage = 100;
578 bolt->splashDamage = 100;
579 bolt->splashRadius = 150;
580 bolt->methodOfDeath = MOD_GRENADE;
581 bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH;
582 bolt->clipmask = MASK_SHOT;
583 bolt->target_ent = NULL;
584
585 bolt->s.pos.trType = TR_GRAVITY;
586 bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
587 VectorCopy( start, bolt->s.pos.trBase );
588 VectorScale( dir, 700, bolt->s.pos.trDelta );
589 SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
590
591 VectorCopy (start, bolt->r.currentOrigin);
592
593 return bolt;
594 }
595
596 //=============================================================================
597
598
599 /*
600 =================
601 fire_bfg
602 =================
603 */
fire_bfg(gentity_t * self,vec3_t start,vec3_t dir)604 gentity_t *fire_bfg (gentity_t *self, vec3_t start, vec3_t dir) {
605 gentity_t *bolt;
606
607 VectorNormalize (dir);
608
609 bolt = G_Spawn();
610 bolt->classname = "bfg";
611 bolt->nextthink = level.time + 10000;
612 bolt->think = G_ExplodeMissile;
613 bolt->s.eType = ET_MISSILE;
614 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
615 bolt->s.weapon = WP_BFG;
616 bolt->r.ownerNum = self->s.number;
617 bolt->parent = self;
618 bolt->damage = 100;
619 bolt->splashDamage = 100;
620 bolt->splashRadius = 120;
621 bolt->methodOfDeath = MOD_BFG;
622 bolt->splashMethodOfDeath = MOD_BFG_SPLASH;
623 bolt->clipmask = MASK_SHOT;
624 bolt->target_ent = NULL;
625
626 bolt->s.pos.trType = TR_LINEAR;
627 bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
628 VectorCopy( start, bolt->s.pos.trBase );
629 VectorScale( dir, 2000, bolt->s.pos.trDelta );
630 SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
631 VectorCopy (start, bolt->r.currentOrigin);
632
633 return bolt;
634 }
635
636 //=============================================================================
637
638
639 /*
640 =================
641 fire_rocket
642 =================
643 */
fire_rocket(gentity_t * self,vec3_t start,vec3_t dir)644 gentity_t *fire_rocket (gentity_t *self, vec3_t start, vec3_t dir) {
645 gentity_t *bolt;
646
647 VectorNormalize (dir);
648
649 bolt = G_Spawn();
650 bolt->classname = "rocket";
651 bolt->nextthink = level.time + 15000;
652 bolt->think = G_ExplodeMissile;
653 bolt->s.eType = ET_MISSILE;
654 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
655 bolt->s.weapon = WP_ROCKET_LAUNCHER;
656 bolt->r.ownerNum = self->s.number;
657 bolt->parent = self;
658 bolt->damage = 100;
659 bolt->splashDamage = 100;
660 bolt->splashRadius = 120;
661 bolt->methodOfDeath = MOD_ROCKET;
662 bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH;
663 bolt->clipmask = MASK_SHOT;
664 bolt->target_ent = NULL;
665
666 bolt->s.pos.trType = TR_LINEAR;
667 bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
668 VectorCopy( start, bolt->s.pos.trBase );
669 VectorScale( dir, 900, bolt->s.pos.trDelta );
670 SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
671 VectorCopy (start, bolt->r.currentOrigin);
672
673 return bolt;
674 }
675
676 /*
677 =================
678 fire_grapple
679 =================
680 */
fire_grapple(gentity_t * self,vec3_t start,vec3_t dir)681 gentity_t *fire_grapple (gentity_t *self, vec3_t start, vec3_t dir) {
682 gentity_t *hook;
683
684 VectorNormalize (dir);
685
686 hook = G_Spawn();
687 hook->classname = "hook";
688 hook->nextthink = level.time + 10000;
689 hook->think = Weapon_HookFree;
690 hook->s.eType = ET_MISSILE;
691 hook->r.svFlags = SVF_USE_CURRENT_ORIGIN;
692 hook->s.weapon = WP_GRAPPLING_HOOK;
693 hook->r.ownerNum = self->s.number;
694 hook->methodOfDeath = MOD_GRAPPLE;
695 hook->clipmask = MASK_SHOT;
696 hook->parent = self;
697 hook->target_ent = NULL;
698
699 hook->s.pos.trType = TR_LINEAR;
700 hook->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
701 hook->s.otherEntityNum = self->s.number; // use to match beam in client
702 VectorCopy( start, hook->s.pos.trBase );
703 VectorScale( dir, 800, hook->s.pos.trDelta );
704 SnapVector( hook->s.pos.trDelta ); // save net bandwidth
705 VectorCopy (start, hook->r.currentOrigin);
706
707 self->client->hook = hook;
708
709 return hook;
710 }
711
712
713 #ifdef MISSIONPACK
714 /*
715 =================
716 fire_nail
717 =================
718 */
719 #define NAILGUN_SPREAD 500
720
fire_nail(gentity_t * self,vec3_t start,vec3_t forward,vec3_t right,vec3_t up)721 gentity_t *fire_nail( gentity_t *self, vec3_t start, vec3_t forward, vec3_t right, vec3_t up ) {
722 gentity_t *bolt;
723 vec3_t dir;
724 vec3_t end;
725 float r, u, scale;
726
727 bolt = G_Spawn();
728 bolt->classname = "nail";
729 bolt->nextthink = level.time + 10000;
730 bolt->think = G_ExplodeMissile;
731 bolt->s.eType = ET_MISSILE;
732 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
733 bolt->s.weapon = WP_NAILGUN;
734 bolt->r.ownerNum = self->s.number;
735 bolt->parent = self;
736 bolt->damage = 20;
737 bolt->methodOfDeath = MOD_NAIL;
738 bolt->clipmask = MASK_SHOT;
739 bolt->target_ent = NULL;
740
741 bolt->s.pos.trType = TR_LINEAR;
742 bolt->s.pos.trTime = level.time;
743 VectorCopy( start, bolt->s.pos.trBase );
744
745 r = random() * M_PI * 2.0f;
746 u = sin(r) * crandom() * NAILGUN_SPREAD * 16;
747 r = cos(r) * crandom() * NAILGUN_SPREAD * 16;
748 VectorMA( start, 8192 * 16, forward, end);
749 VectorMA (end, r, right, end);
750 VectorMA (end, u, up, end);
751 VectorSubtract( end, start, dir );
752 VectorNormalize( dir );
753
754 scale = 555 + random() * 1800;
755 VectorScale( dir, scale, bolt->s.pos.trDelta );
756 SnapVector( bolt->s.pos.trDelta );
757
758 VectorCopy( start, bolt->r.currentOrigin );
759
760 return bolt;
761 }
762
763
764 /*
765 =================
766 fire_prox
767 =================
768 */
fire_prox(gentity_t * self,vec3_t start,vec3_t dir)769 gentity_t *fire_prox( gentity_t *self, vec3_t start, vec3_t dir ) {
770 gentity_t *bolt;
771
772 VectorNormalize (dir);
773
774 bolt = G_Spawn();
775 bolt->classname = "prox mine";
776 bolt->nextthink = level.time + 3000;
777 bolt->think = G_ExplodeMissile;
778 bolt->s.eType = ET_MISSILE;
779 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
780 bolt->s.weapon = WP_PROX_LAUNCHER;
781 bolt->s.eFlags = 0;
782 bolt->r.ownerNum = self->s.number;
783 bolt->parent = self;
784 bolt->damage = 0;
785 bolt->splashDamage = 100;
786 bolt->splashRadius = 150;
787 bolt->methodOfDeath = MOD_PROXIMITY_MINE;
788 bolt->splashMethodOfDeath = MOD_PROXIMITY_MINE;
789 bolt->clipmask = MASK_SHOT;
790 bolt->target_ent = NULL;
791 // count is used to check if the prox mine left the player bbox
792 // if count == 1 then the prox mine left the player bbox and can attack to it
793 bolt->count = 0;
794
795 //FIXME: we prolly wanna abuse another field
796 bolt->s.generic1 = self->client->sess.sessionTeam;
797
798 bolt->s.pos.trType = TR_GRAVITY;
799 bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
800 VectorCopy( start, bolt->s.pos.trBase );
801 VectorScale( dir, 700, bolt->s.pos.trDelta );
802 SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
803
804 VectorCopy (start, bolt->r.currentOrigin);
805
806 return bolt;
807 }
808 #endif
809