1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2000-2006 Tim Angus
5 
6 This file is part of Tremulous.
7 
8 Tremulous is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12 
13 Tremulous is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Tremulous; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 ===========================================================================
22 */
23 
24 #include "g_local.h"
25 
26 #define MISSILE_PRESTEP_TIME  50
27 
28 /*
29 ================
30 G_BounceMissile
31 
32 ================
33 */
G_BounceMissile(gentity_t * ent,trace_t * trace)34 void G_BounceMissile( gentity_t *ent, trace_t *trace )
35 {
36   vec3_t  velocity;
37   float dot;
38   int   hitTime;
39 
40   // reflect the velocity on the trace plane
41   hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
42   BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
43   dot = DotProduct( velocity, trace->plane.normal );
44   VectorMA( velocity, -2 * dot, trace->plane.normal, ent->s.pos.trDelta );
45 
46   if( ent->s.eFlags & EF_BOUNCE_HALF )
47   {
48     VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta );
49     // check for stop
50     if( trace->plane.normal[ 2 ] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 )
51     {
52       G_SetOrigin( ent, trace->endpos );
53       return;
54     }
55   }
56 
57   VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin );
58   VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
59   ent->s.pos.trTime = level.time;
60 }
61 
62 
63 /*
64 ================
65 G_ExplodeMissile
66 
67 Explode a missile without an impact
68 ================
69 */
G_ExplodeMissile(gentity_t * ent)70 void G_ExplodeMissile( gentity_t *ent )
71 {
72   vec3_t    dir;
73   vec3_t    origin;
74 
75   BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
76   SnapVector( origin );
77   G_SetOrigin( ent, origin );
78 
79   // we don't have a valid direction, so just point straight up
80   dir[ 0 ] = dir[ 1 ] = 0;
81   dir[ 2 ] = 1;
82 
83   ent->s.eType = ET_GENERAL;
84 
85   //TA: tired... can't be fucked... hack
86   if( ent->s.weapon != WP_LOCKBLOB_LAUNCHER &&
87       ent->s.weapon != WP_FLAMER )
88     G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) );
89 
90   ent->freeAfterEvent = qtrue;
91 
92   // splash damage
93   if( ent->splashDamage )
94     G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage,
95                     ent->splashRadius, ent, ent->splashMethodOfDeath );
96 
97   trap_LinkEntity( ent );
98 }
99 
100 void AHive_ReturnToHive( gentity_t *self );
101 
102 /*
103 ================
104 G_MissileImpact
105 
106 ================
107 */
G_MissileImpact(gentity_t * ent,trace_t * trace)108 void G_MissileImpact( gentity_t *ent, trace_t *trace )
109 {
110   gentity_t   *other, *attacker;
111   qboolean    returnAfterDamage = qfalse;
112   vec3_t      dir;
113 
114   other = &g_entities[ trace->entityNum ];
115   attacker = &g_entities[ ent->r.ownerNum ];
116 
117   // check for bounce
118   if( !other->takedamage &&
119       ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) )
120   {
121     G_BounceMissile( ent, trace );
122 
123     //only play a sound if requested
124     if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) )
125       G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
126 
127     return;
128   }
129 
130   if( !strcmp( ent->classname, "grenade" ) )
131   {
132     //grenade doesn't explode on impact
133     G_BounceMissile( ent, trace );
134 
135     //only play a sound if requested
136     if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) )
137       G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
138 
139     return;
140   }
141   else if( !strcmp( ent->classname, "lockblob" ) )
142   {
143     if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
144     {
145       other->client->ps.stats[ STAT_STATE ] |= SS_BLOBLOCKED;
146       other->client->lastLockTime = level.time;
147       AngleVectors( other->client->ps.viewangles, dir, NULL, NULL );
148       other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir );
149     }
150   }
151   else if( !strcmp( ent->classname, "slowblob" ) )
152   {
153     if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
154     {
155       other->client->ps.stats[ STAT_STATE ] |= SS_SLOWLOCKED;
156       other->client->lastSlowTime = level.time;
157       AngleVectors( other->client->ps.viewangles, dir, NULL, NULL );
158       other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir );
159     }
160   }
161   else if( !strcmp( ent->classname, "hive" ) )
162   {
163     if( other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_HIVE )
164     {
165       if( !ent->parent )
166         G_Printf( S_COLOR_YELLOW "WARNING: hive entity has no parent in G_MissileImpact\n" );
167       else
168         ent->parent->active = qfalse;
169 
170       G_FreeEntity( ent );
171       return;
172     }
173     else
174     {
175       //prevent collision with the client when returning
176       ent->r.ownerNum = other->s.number;
177 
178       ent->think = AHive_ReturnToHive;
179       ent->nextthink = level.time + FRAMETIME;
180 
181       //only damage humans
182       if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
183         returnAfterDamage = qtrue;
184       else
185         return;
186     }
187   }
188 
189   // impact damage
190   if( other->takedamage )
191   {
192     // FIXME: wrong damage direction?
193     if( ent->damage )
194     {
195       vec3_t  velocity;
196 
197       BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
198       if( VectorLength( velocity ) == 0 )
199         velocity[ 2 ] = 1;  // stepped on a grenade
200 
201       G_Damage( other, ent, attacker, velocity, ent->s.origin, ent->damage,
202         0, ent->methodOfDeath );
203     }
204   }
205 
206   if( returnAfterDamage )
207     return;
208 
209   // is it cheaper in bandwidth to just remove this ent and create a new
210   // one, rather than changing the missile into the explosion?
211 
212   if( other->takedamage && other->client )
213   {
214     G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
215     ent->s.otherEntityNum = other->s.number;
216   }
217   else if( trace->surfaceFlags & SURF_METALSTEPS )
218     G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) );
219   else
220     G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
221 
222   ent->freeAfterEvent = qtrue;
223 
224   // change over to a normal entity right at the point of impact
225   ent->s.eType = ET_GENERAL;
226 
227   SnapVectorTowards( trace->endpos, ent->s.pos.trBase );  // save net bandwidth
228 
229   G_SetOrigin( ent, trace->endpos );
230 
231   // splash damage (doesn't apply to person directly hit)
232   if( ent->splashDamage )
233     G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
234                     other, ent->splashMethodOfDeath );
235 
236   trap_LinkEntity( ent );
237 }
238 
239 
240 /*
241 ================
242 G_RunMissile
243 
244 ================
245 */
G_RunMissile(gentity_t * ent)246 void G_RunMissile( gentity_t *ent )
247 {
248   vec3_t    origin;
249   trace_t   tr;
250   int     passent;
251 
252   // get current position
253   BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
254 
255   // ignore interactions with the missile owner
256   passent = ent->r.ownerNum;
257 
258   // trace a line from the previous position to the current position
259   trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask );
260 
261   if( tr.startsolid || tr.allsolid )
262   {
263     // make sure the tr.entityNum is set to the entity we're stuck in
264     trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask );
265     tr.fraction = 0;
266   }
267   else
268     VectorCopy( tr.endpos, ent->r.currentOrigin );
269 
270   ent->r.contents = CONTENTS_SOLID; //trick trap_LinkEntity into...
271   trap_LinkEntity( ent );
272   ent->r.contents = 0; //...encoding bbox information
273 
274   if( tr.fraction != 1 )
275   {
276     // never explode or bounce on sky
277     if( tr.surfaceFlags & SURF_NOIMPACT )
278     {
279       // If grapple, reset owner
280       if( ent->parent && ent->parent->client && ent->parent->client->hook == ent )
281         ent->parent->client->hook = NULL;
282 
283       G_FreeEntity( ent );
284       return;
285     }
286 
287     G_MissileImpact( ent, &tr );
288     if( ent->s.eType != ET_MISSILE )
289       return;   // exploded
290   }
291 
292   // check think function after bouncing
293   G_RunThink( ent );
294 }
295 
296 
297 //=============================================================================
298 
299 /*
300 =================
301 fire_flamer
302 
303 =================
304 */
fire_flamer(gentity_t * self,vec3_t start,vec3_t dir)305 gentity_t *fire_flamer( gentity_t *self, vec3_t start, vec3_t dir )
306 {
307   gentity_t *bolt;
308   vec3_t    pvel;
309 
310   VectorNormalize (dir);
311 
312   bolt = G_Spawn();
313   bolt->classname = "flame";
314   bolt->nextthink = level.time + FLAMER_LIFETIME;
315   bolt->think = G_ExplodeMissile;
316   bolt->s.eType = ET_MISSILE;
317   bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
318   bolt->s.weapon = WP_FLAMER;
319   bolt->s.generic1 = self->s.generic1; //weaponMode
320   bolt->r.ownerNum = self->s.number;
321   bolt->parent = self;
322   bolt->damage = FLAMER_DMG;
323   bolt->splashDamage = FLAMER_DMG;
324   bolt->splashRadius = FLAMER_RADIUS;
325   bolt->methodOfDeath = MOD_FLAMER;
326   bolt->splashMethodOfDeath = MOD_FLAMER_SPLASH;
327   bolt->clipmask = MASK_SHOT;
328   bolt->target_ent = NULL;
329   bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -15.0f;
330   bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 15.0f;
331 
332   bolt->s.pos.trType = TR_LINEAR;
333   bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame
334   VectorCopy( start, bolt->s.pos.trBase );
335   VectorScale( self->client->ps.velocity, FLAMER_LAG, pvel );
336   VectorMA( pvel, FLAMER_SPEED, dir, bolt->s.pos.trDelta );
337   SnapVector( bolt->s.pos.trDelta );      // save net bandwidth
338 
339   VectorCopy( start, bolt->r.currentOrigin );
340 
341   return bolt;
342 }
343 
344 //=============================================================================
345 
346 /*
347 =================
348 fire_blaster
349 
350 =================
351 */
fire_blaster(gentity_t * self,vec3_t start,vec3_t dir)352 gentity_t *fire_blaster( gentity_t *self, vec3_t start, vec3_t dir )
353 {
354   gentity_t *bolt;
355 
356   VectorNormalize (dir);
357 
358   bolt = G_Spawn();
359   bolt->classname = "blaster";
360   bolt->nextthink = level.time + 10000;
361   bolt->think = G_ExplodeMissile;
362   bolt->s.eType = ET_MISSILE;
363   bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
364   bolt->s.weapon = WP_BLASTER;
365   bolt->s.generic1 = self->s.generic1; //weaponMode
366   bolt->r.ownerNum = self->s.number;
367   bolt->parent = self;
368   bolt->damage = BLASTER_DMG;
369   bolt->splashDamage = 0;
370   bolt->splashRadius = 0;
371   bolt->methodOfDeath = MOD_BLASTER;
372   bolt->splashMethodOfDeath = MOD_BLASTER;
373   bolt->clipmask = MASK_SHOT;
374   bolt->target_ent = NULL;
375 
376   bolt->s.pos.trType = TR_LINEAR;
377   bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame
378   VectorCopy( start, bolt->s.pos.trBase );
379   VectorScale( dir, BLASTER_SPEED, bolt->s.pos.trDelta );
380   SnapVector( bolt->s.pos.trDelta );      // save net bandwidth
381 
382   VectorCopy( start, bolt->r.currentOrigin );
383 
384   return bolt;
385 }
386 
387 //=============================================================================
388 
389 /*
390 =================
391 fire_pulseRifle
392 
393 =================
394 */
fire_pulseRifle(gentity_t * self,vec3_t start,vec3_t dir)395 gentity_t *fire_pulseRifle( gentity_t *self, vec3_t start, vec3_t dir )
396 {
397   gentity_t *bolt;
398 
399   VectorNormalize (dir);
400 
401   bolt = G_Spawn();
402   bolt->classname = "pulse";
403   bolt->nextthink = level.time + 10000;
404   bolt->think = G_ExplodeMissile;
405   bolt->s.eType = ET_MISSILE;
406   bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
407   bolt->s.weapon = WP_PULSE_RIFLE;
408   bolt->s.generic1 = self->s.generic1; //weaponMode
409   bolt->r.ownerNum = self->s.number;
410   bolt->parent = self;
411   bolt->damage = PRIFLE_DMG;
412   bolt->splashDamage = 0;
413   bolt->splashRadius = 0;
414   bolt->methodOfDeath = MOD_PRIFLE;
415   bolt->splashMethodOfDeath = MOD_PRIFLE;
416   bolt->clipmask = MASK_SHOT;
417   bolt->target_ent = NULL;
418 
419   bolt->s.pos.trType = TR_LINEAR;
420   bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame
421   VectorCopy( start, bolt->s.pos.trBase );
422   VectorScale( dir, PRIFLE_SPEED, bolt->s.pos.trDelta );
423   SnapVector( bolt->s.pos.trDelta );      // save net bandwidth
424 
425   VectorCopy( start, bolt->r.currentOrigin );
426 
427   return bolt;
428 }
429 
430 //=============================================================================
431 
432 /*
433 =================
434 fire_luciferCannon
435 
436 =================
437 */
fire_luciferCannon(gentity_t * self,vec3_t start,vec3_t dir,int damage,int radius)438 gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir, int damage, int radius )
439 {
440   gentity_t *bolt;
441   int localDamage = (int)( ceil( ( (float)damage /
442                                    (float)LCANNON_TOTAL_CHARGE ) * (float)LCANNON_DAMAGE ) );
443 
444   VectorNormalize( dir );
445 
446   bolt = G_Spawn( );
447   bolt->classname = "lcannon";
448 
449   if( damage == LCANNON_TOTAL_CHARGE )
450     bolt->nextthink = level.time;
451   else
452     bolt->nextthink = level.time + 10000;
453 
454   bolt->think = G_ExplodeMissile;
455   bolt->s.eType = ET_MISSILE;
456   bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
457   bolt->s.weapon = WP_LUCIFER_CANNON;
458   bolt->s.generic1 = self->s.generic1; //weaponMode
459   bolt->r.ownerNum = self->s.number;
460   bolt->parent = self;
461   bolt->damage = localDamage;
462   bolt->splashDamage = localDamage / 2;
463   bolt->splashRadius = radius;
464   bolt->methodOfDeath = MOD_LCANNON;
465   bolt->splashMethodOfDeath = MOD_LCANNON_SPLASH;
466   bolt->clipmask = MASK_SHOT;
467   bolt->target_ent = NULL;
468 
469   bolt->s.pos.trType = TR_LINEAR;
470   bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame
471   VectorCopy( start, bolt->s.pos.trBase );
472   VectorScale( dir, LCANNON_SPEED, bolt->s.pos.trDelta );
473   SnapVector( bolt->s.pos.trDelta );      // save net bandwidth
474 
475   VectorCopy( start, bolt->r.currentOrigin );
476 
477   return bolt;
478 }
479 
480 /*
481 =================
482 launch_grenade
483 
484 =================
485 */
launch_grenade(gentity_t * self,vec3_t start,vec3_t dir)486 gentity_t *launch_grenade( gentity_t *self, vec3_t start, vec3_t dir )
487 {
488   gentity_t *bolt;
489 
490   VectorNormalize( dir );
491 
492   bolt = G_Spawn( );
493   bolt->classname = "grenade";
494   bolt->nextthink = level.time + 5000;
495   bolt->think = G_ExplodeMissile;
496   bolt->s.eType = ET_MISSILE;
497   bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
498   bolt->s.weapon = WP_GRENADE;
499   bolt->s.eFlags = EF_BOUNCE_HALF;
500   bolt->s.generic1 = WPM_PRIMARY; //weaponMode
501   bolt->r.ownerNum = self->s.number;
502   bolt->parent = self;
503   bolt->damage = GRENADE_DAMAGE;
504   bolt->splashDamage = GRENADE_DAMAGE;
505   bolt->splashRadius = GRENADE_RANGE;
506   bolt->methodOfDeath = MOD_GRENADE;
507   bolt->splashMethodOfDeath = MOD_GRENADE;
508   bolt->clipmask = MASK_SHOT;
509   bolt->target_ent = NULL;
510   bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -3.0f;
511   bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 3.0f;
512   bolt->s.time = level.time;
513 
514   bolt->s.pos.trType = TR_GRAVITY;
515   bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame
516   VectorCopy( start, bolt->s.pos.trBase );
517   VectorScale( dir, GRENADE_SPEED, bolt->s.pos.trDelta );
518   SnapVector( bolt->s.pos.trDelta );      // save net bandwidth
519 
520   VectorCopy( start, bolt->r.currentOrigin );
521 
522   return bolt;
523 }
524 //=============================================================================
525 
526 /*
527 ================
528 AHive_ReturnToHive
529 
530 Adjust the trajectory to point towards the hive
531 ================
532 */
AHive_ReturnToHive(gentity_t * self)533 void AHive_ReturnToHive( gentity_t *self )
534 {
535   vec3_t  dir;
536   trace_t tr;
537 
538   if( !self->parent )
539   {
540     G_Printf( S_COLOR_YELLOW "WARNING: AHive_ReturnToHive called with no self->parent\n" );
541     return;
542   }
543 
544   trap_UnlinkEntity( self->parent );
545   trap_Trace( &tr, self->r.currentOrigin, self->r.mins, self->r.maxs,
546               self->parent->r.currentOrigin, self->r.ownerNum, self->clipmask );
547   trap_LinkEntity( self->parent );
548 
549   if( tr.fraction < 1.0f )
550   {
551     //if can't see hive then disperse
552     VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
553     self->s.pos.trType = TR_STATIONARY;
554     self->s.pos.trTime = level.time;
555 
556     self->think = G_ExplodeMissile;
557     self->nextthink = level.time + 2000;
558     self->parent->active = qfalse; //allow the parent to start again
559   }
560   else
561   {
562     VectorSubtract( self->parent->r.currentOrigin, self->r.currentOrigin, dir );
563     VectorNormalize( dir );
564 
565     //change direction towards the hive
566     VectorScale( dir, HIVE_SPEED, self->s.pos.trDelta );
567     SnapVector( self->s.pos.trDelta );      // save net bandwidth
568     VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
569     self->s.pos.trTime = level.time;
570 
571     self->think = G_ExplodeMissile;
572     self->nextthink = level.time + 15000;
573   }
574 }
575 
576 /*
577 ================
578 AHive_SearchAndDestroy
579 
580 Adjust the trajectory to point towards the target
581 ================
582 */
AHive_SearchAndDestroy(gentity_t * self)583 void AHive_SearchAndDestroy( gentity_t *self )
584 {
585   vec3_t dir;
586   trace_t tr;
587 
588   trap_Trace( &tr, self->r.currentOrigin, self->r.mins, self->r.maxs,
589               self->target_ent->r.currentOrigin, self->r.ownerNum, self->clipmask );
590 
591   //if there is no LOS or the parent hive is too far away or the target is dead, return
592   if( tr.entityNum == ENTITYNUM_WORLD ||
593       Distance( self->r.currentOrigin, self->parent->r.currentOrigin ) > ( HIVE_RANGE * 5 ) ||
594       self->target_ent->health <= 0 )
595   {
596     self->r.ownerNum = ENTITYNUM_WORLD;
597 
598     self->think = AHive_ReturnToHive;
599     self->nextthink = level.time + FRAMETIME;
600   }
601   else
602   {
603     VectorSubtract( self->target_ent->r.currentOrigin, self->r.currentOrigin, dir );
604     VectorNormalize( dir );
605 
606     //change direction towards the player
607     VectorScale( dir, HIVE_SPEED, self->s.pos.trDelta );
608     SnapVector( self->s.pos.trDelta );      // save net bandwidth
609     VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
610     self->s.pos.trTime = level.time;
611 
612     self->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD;
613   }
614 }
615 
616 /*
617 =================
618 fire_hive
619 =================
620 */
fire_hive(gentity_t * self,vec3_t start,vec3_t dir)621 gentity_t *fire_hive( gentity_t *self, vec3_t start, vec3_t dir )
622 {
623   gentity_t *bolt;
624 
625   VectorNormalize ( dir );
626 
627   bolt = G_Spawn( );
628   bolt->classname = "hive";
629   bolt->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD;
630   bolt->think = AHive_SearchAndDestroy;
631   bolt->s.eType = ET_MISSILE;
632   bolt->s.eFlags |= EF_BOUNCE|EF_NO_BOUNCE_SOUND;
633   bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
634   bolt->s.weapon = WP_HIVE;
635   bolt->s.generic1 = WPM_PRIMARY; //weaponMode
636   bolt->r.ownerNum = self->s.number;
637   bolt->parent = self;
638   bolt->damage = HIVE_DMG;
639   bolt->splashDamage = 0;
640   bolt->splashRadius = 0;
641   bolt->methodOfDeath = MOD_SWARM;
642   bolt->clipmask = MASK_SHOT;
643   bolt->target_ent = self->target_ent;
644 
645   bolt->s.pos.trType = TR_LINEAR;
646   bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame
647   VectorCopy( start, bolt->s.pos.trBase );
648   VectorScale( dir, HIVE_SPEED, bolt->s.pos.trDelta );
649   SnapVector( bolt->s.pos.trDelta );      // save net bandwidth
650   VectorCopy( start, bolt->r.currentOrigin );
651 
652   return bolt;
653 }
654 
655 //=============================================================================
656 
657 /*
658 =================
659 fire_lockblob
660 =================
661 */
fire_lockblob(gentity_t * self,vec3_t start,vec3_t dir)662 gentity_t *fire_lockblob( gentity_t *self, vec3_t start, vec3_t dir )
663 {
664   gentity_t *bolt;
665 
666   VectorNormalize ( dir );
667 
668   bolt = G_Spawn( );
669   bolt->classname = "lockblob";
670   bolt->nextthink = level.time + 15000;
671   bolt->think = G_ExplodeMissile;
672   bolt->s.eType = ET_MISSILE;
673   bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
674   bolt->s.weapon = WP_LOCKBLOB_LAUNCHER;
675   bolt->s.generic1 = WPM_PRIMARY; //weaponMode
676   bolt->r.ownerNum = self->s.number;
677   bolt->parent = self;
678   bolt->damage = 0;
679   bolt->splashDamage = 0;
680   bolt->splashRadius = 0;
681   bolt->methodOfDeath = MOD_UNKNOWN; //doesn't do damage so will never kill
682   bolt->clipmask = MASK_SHOT;
683   bolt->target_ent = NULL;
684 
685   bolt->s.pos.trType = TR_LINEAR;
686   bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame
687   VectorCopy( start, bolt->s.pos.trBase );
688   VectorScale( dir, 500, bolt->s.pos.trDelta );
689   SnapVector( bolt->s.pos.trDelta );      // save net bandwidth
690   VectorCopy( start, bolt->r.currentOrigin );
691 
692   return bolt;
693 }
694 
695 /*
696 =================
697 fire_slowBlob
698 =================
699 */
fire_slowBlob(gentity_t * self,vec3_t start,vec3_t dir)700 gentity_t *fire_slowBlob( gentity_t *self, vec3_t start, vec3_t dir )
701 {
702   gentity_t *bolt;
703 
704   VectorNormalize ( dir );
705 
706   bolt = G_Spawn( );
707   bolt->classname = "slowblob";
708   bolt->nextthink = level.time + 15000;
709   bolt->think = G_ExplodeMissile;
710   bolt->s.eType = ET_MISSILE;
711   bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
712   bolt->s.weapon = WP_ABUILD2;
713   bolt->s.generic1 = self->s.generic1; //weaponMode
714   bolt->r.ownerNum = self->s.number;
715   bolt->parent = self;
716   bolt->damage = ABUILDER_BLOB_DMG;
717   bolt->splashDamage = 0;
718   bolt->splashRadius = 0;
719   bolt->methodOfDeath = MOD_SLOWBLOB;
720   bolt->splashMethodOfDeath = MOD_SLOWBLOB;
721   bolt->clipmask = MASK_SHOT;
722   bolt->target_ent = NULL;
723 
724   bolt->s.pos.trType = TR_GRAVITY;
725   bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame
726   VectorCopy( start, bolt->s.pos.trBase );
727   VectorScale( dir, ABUILDER_BLOB_SPEED, bolt->s.pos.trDelta );
728   SnapVector( bolt->s.pos.trDelta );      // save net bandwidth
729   VectorCopy( start, bolt->r.currentOrigin );
730 
731   return bolt;
732 }
733 
734 /*
735 =================
736 fire_paraLockBlob
737 =================
738 */
fire_paraLockBlob(gentity_t * self,vec3_t start,vec3_t dir)739 gentity_t *fire_paraLockBlob( gentity_t *self, vec3_t start, vec3_t dir )
740 {
741   gentity_t *bolt;
742 
743   VectorNormalize ( dir );
744 
745   bolt = G_Spawn( );
746   bolt->classname = "lockblob";
747   bolt->nextthink = level.time + 15000;
748   bolt->think = G_ExplodeMissile;
749   bolt->s.eType = ET_MISSILE;
750   bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
751   bolt->s.weapon = WP_LOCKBLOB_LAUNCHER;
752   bolt->s.generic1 = self->s.generic1; //weaponMode
753   bolt->r.ownerNum = self->s.number;
754   bolt->parent = self;
755   bolt->damage = 0;
756   bolt->splashDamage = 0;
757   bolt->splashRadius = 0;
758   bolt->clipmask = MASK_SHOT;
759   bolt->target_ent = NULL;
760 
761   bolt->s.pos.trType = TR_GRAVITY;
762   bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame
763   VectorCopy( start, bolt->s.pos.trBase );
764   VectorScale( dir, LOCKBLOB_SPEED, bolt->s.pos.trDelta );
765   SnapVector( bolt->s.pos.trDelta );      // save net bandwidth
766   VectorCopy( start, bolt->r.currentOrigin );
767 
768   return bolt;
769 }
770 
771 /*
772 =================
773 fire_bounceBall
774 =================
775 */
fire_bounceBall(gentity_t * self,vec3_t start,vec3_t dir)776 gentity_t *fire_bounceBall( gentity_t *self, vec3_t start, vec3_t dir )
777 {
778   gentity_t *bolt;
779 
780   VectorNormalize ( dir );
781 
782   bolt = G_Spawn( );
783   bolt->classname = "bounceball";
784   bolt->nextthink = level.time + 3000;
785   bolt->think = G_ExplodeMissile;
786   bolt->s.eType = ET_MISSILE;
787   bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
788   bolt->s.weapon = WP_ALEVEL3_UPG;
789   bolt->s.generic1 = self->s.generic1; //weaponMode
790   bolt->r.ownerNum = self->s.number;
791   bolt->parent = self;
792   bolt->damage = LEVEL3_BOUNCEBALL_DMG;
793   bolt->splashDamage = 0;
794   bolt->splashRadius = 0;
795   bolt->methodOfDeath = MOD_LEVEL3_BOUNCEBALL;
796   bolt->splashMethodOfDeath = MOD_LEVEL3_BOUNCEBALL;
797   bolt->clipmask = MASK_SHOT;
798   bolt->target_ent = NULL;
799 
800   bolt->s.pos.trType = TR_GRAVITY;
801   bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame
802   VectorCopy( start, bolt->s.pos.trBase );
803   VectorScale( dir, LEVEL3_BOUNCEBALL_SPEED, bolt->s.pos.trDelta );
804   SnapVector( bolt->s.pos.trDelta );      // save net bandwidth
805   VectorCopy( start, bolt->r.currentOrigin );
806   /*bolt->s.eFlags |= EF_BOUNCE;*/
807 
808   return bolt;
809 }
810 
811