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