1 /*
2 * Copyright(c) 1997-2001 Id Software, Inc.
3 * Copyright(c) 2002 The Quakeforge Project.
4 * Copyright(c) 2006 Quetoo.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or(at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include "g_local.h"
23
24
25 /*
26 fire_hit
27
28 Used for all impact(hit/punch/slash) attacks
29 */
fire_hit(edict_t * self,vec3_t aim,int damage,int kick)30 qboolean fire_hit(edict_t *self, vec3_t aim, int damage, int kick){
31 return false;
32 }
33
34
35 /*
36 fire_lead
37
38 This is an internal support routine used for bullet/pellet based weapons.
39 */
fire_lead(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int kick,int te_impact,int hspread,int vspread,int mod)40 static void fire_lead(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod){
41 trace_t tr;
42 vec3_t dir;
43 vec3_t forward, right, up;
44 vec3_t end;
45 float r;
46 float u;
47 vec3_t water_start;
48 qboolean water = false;
49 int content_mask = MASK_SHOT | MASK_WATER;
50
51 tr = gi.trace(self->s.origin, NULL, NULL, start, self, MASK_SHOT);
52 if(!(tr.fraction < 1.0)){
53 vectoangles(aimdir, dir);
54 AngleVectors(dir, forward, right, up);
55
56 r = crandom() * hspread;
57 u = crandom() * vspread;
58 VectorMA(start, 8192, forward, end);
59 VectorMA(end, r, right, end);
60 VectorMA(end, u, up, end);
61
62 if(gi.pointcontents(start) & MASK_WATER){
63 water = true;
64 VectorCopy(start, water_start);
65 content_mask &= ~MASK_WATER;
66 }
67
68 tr = gi.trace(start, NULL, NULL, end, self, content_mask);
69
70 // see if we hit water
71 if(tr.contents & MASK_WATER){
72 int color;
73
74 water = true;
75 VectorCopy(tr.endpos, water_start);
76
77 if(!VectorCompare(start, tr.endpos)){
78 if(tr.contents & CONTENTS_WATER){
79 if(strcmp(tr.surface->name, "*brwater") == 0)
80 color = SPLASH_BROWN_WATER;
81 else
82 color = SPLASH_BLUE_WATER;
83 } else if(tr.contents & CONTENTS_SLIME)
84 color = SPLASH_SLIME;
85 else if(tr.contents & CONTENTS_LAVA)
86 color = SPLASH_LAVA;
87 else
88 color = SPLASH_UNKNOWN;
89
90 if(color != SPLASH_UNKNOWN){
91 gi.WriteByte(svc_temp_entity);
92 gi.WriteByte(TE_SPLASH);
93 gi.WriteByte(8);
94 gi.WritePosition(tr.endpos);
95 gi.WriteDir(tr.plane.normal);
96 gi.WriteByte(color);
97 gi.multicast(tr.endpos, MULTICAST_PVS);
98 }
99
100 // change bullet's course when it enters water
101 VectorSubtract(end, start, dir);
102 vectoangles(dir, dir);
103 AngleVectors(dir, forward, right, up);
104 r = crandom() * hspread * 2;
105 u = crandom() * vspread * 2;
106 VectorMA(water_start, 8192, forward, end);
107 VectorMA(end, r, right, end);
108 VectorMA(end, u, up, end);
109 }
110
111 // re-trace ignoring water this time
112 tr = gi.trace(water_start, NULL, NULL, end, self, MASK_SHOT);
113 }
114 }
115
116 // send gun puff / flash
117 if(!((tr.surface) &&(tr.surface->flags & SURF_SKY))){
118 if(tr.fraction < 1.0){
119 if(tr.ent->takedamage){
120 T_Damage(tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod);
121 } else {
122 if(strncmp(tr.surface->name, "sky", 3) != 0){
123 gi.WriteByte(svc_temp_entity);
124 gi.WriteByte(te_impact);
125 gi.WritePosition(tr.endpos);
126 gi.WriteDir(tr.plane.normal);
127 gi.multicast(tr.endpos, MULTICAST_PVS);
128
129 if(self->client)
130 PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
131 }
132 }
133 }
134 }
135
136 // if went through water, determine where the end and make a bubble trail
137 if(water){
138 vec3_t pos;
139
140 VectorSubtract(tr.endpos, water_start, dir);
141 VectorNormalize(dir);
142 VectorMA(tr.endpos, -2, dir, pos);
143 if(gi.pointcontents(pos) & MASK_WATER)
144 VectorCopy(pos, tr.endpos);
145 else
146 tr = gi.trace(pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
147
148 VectorAdd(water_start, tr.endpos, pos);
149 VectorScale(pos, 0.5, pos);
150
151 gi.WriteByte(svc_temp_entity);
152 gi.WriteByte(TE_BUBBLETRAIL);
153 gi.WritePosition(water_start);
154 gi.WritePosition(tr.endpos);
155 gi.multicast(pos, MULTICAST_PVS);
156 }
157 }
158
159
160 /*
161 fire_bullet
162
163 Fires a single round. Used for machinegun and chaingun. Would be fine for
164 pistols, rifles, etc....
165 */
fire_bullet(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int kick,int hspread,int vspread,int mod)166 void fire_bullet(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod){
167 fire_lead(self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod);
168 }
169
170
171 /*
172 fire_shotgun
173
174 Shoots shotgun pellets. Used by shotgun and super shotgun.
175 */
fire_shotgun(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int kick,int hspread,int vspread,int count,int mod)176 void fire_shotgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod){
177 int i;
178
179 for(i = 0; i < count; i++)
180 fire_lead(self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod);
181 }
182
183
184 /*
185 fire_blaster
186
187 Fires a single blaster bolt. Used by the blaster and hyper blaster.
188 */
blaster_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)189 void blaster_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
190 int mod;
191
192 if(other == self->owner)
193 return;
194
195 if(surf &&(surf->flags & SURF_SKY)){
196 G_FreeEdict(self);
197 return;
198 }
199
200 if(self->owner->client)
201 PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
202
203 if(other->takedamage){
204 if(self->spawnflags & 1)
205 mod = MOD_HYPERBLASTER;
206 else
207 mod = MOD_BLASTER;
208 T_Damage(other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
209 } else {
210 gi.WriteByte(svc_temp_entity);
211 gi.WriteByte(TE_BLASTER);
212 gi.WritePosition(self->s.origin);
213 if(!plane)
214 gi.WriteDir(vec3_origin);
215 else
216 gi.WriteDir(plane->normal);
217 gi.multicast(self->s.origin, MULTICAST_PVS);
218 }
219
220 G_FreeEdict(self);
221 }
222
fire_blaster(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,int effect,qboolean hyper)223 void fire_blaster(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper){
224 edict_t *bolt;
225 trace_t tr;
226
227 VectorNormalize(dir);
228
229 bolt = G_Spawn();
230 bolt->svflags = SVF_DEADMONSTER;
231 // yes, I know it looks weird that projectiles are deadmonsters
232 // what this means is that when prediction is used against the object
233 //(blaster/hyperblaster shots), the player won't be solid clipped against
234 // the object. Right now trying to run into a firing hyperblaster
235 // is very jerky since you are predicted 'against' the shots.
236 VectorCopy(start, bolt->s.origin);
237 VectorCopy(start, bolt->s.old_origin);
238 vectoangles(dir, bolt->s.angles);
239 VectorScale(dir, speed, bolt->velocity);
240 bolt->movetype = MOVETYPE_FLYMISSILE;
241 bolt->clipmask = MASK_SHOT;
242 bolt->solid = SOLID_BBOX;
243 bolt->s.effects |= effect;
244 VectorClear(bolt->mins);
245 VectorClear(bolt->maxs);
246 bolt->s.modelindex = gi.modelindex("models/objects/laser/tris.md2");
247 bolt->s.sound = gi.soundindex("misc/lasfly.wav");
248 bolt->owner = self;
249 bolt->touch = blaster_touch;
250 bolt->nextthink = level.time + 2;
251 bolt->think = G_FreeEdict;
252 bolt->dmg = damage;
253 bolt->classname = "bolt";
254 bolt->s.renderfx = RF_NOSHADOW;
255 if(hyper)
256 bolt->spawnflags = 1;
257 gi.linkentity(bolt);
258
259 tr = gi.trace(self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
260 if(tr.fraction < 1.0){
261 VectorMA(bolt->s.origin, -10, dir, bolt->s.origin);
262 bolt->touch(bolt, tr.ent, NULL, NULL);
263 }
264 }
265
266
267 /*
268 fire_grenade
269 */
Grenade_Explode(edict_t * ent)270 static void Grenade_Explode(edict_t *ent){
271 vec3_t origin;
272 int mod;
273
274 if(ent->owner->client)
275 PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
276
277 //FIXME: if we are onground then raise our Z just a bit since we are a point?
278 if(ent->enemy){
279 float points;
280 vec3_t v;
281 vec3_t dir;
282
283 VectorAdd(ent->enemy->mins, ent->enemy->maxs, v);
284 VectorMA(ent->enemy->s.origin, 0.5, v, v);
285 VectorSubtract(ent->s.origin, v, v);
286 points = ent->dmg - 0.5 * VectorLength(v);
287 VectorSubtract(ent->enemy->s.origin, ent->s.origin, dir);
288 if(ent->spawnflags & 1)
289 mod = MOD_HANDGRENADE;
290 else
291 mod = MOD_GRENADE;
292 T_Damage(ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin,(int)points,(int)points, DAMAGE_RADIUS, mod);
293 }
294
295 if(ent->spawnflags & 2)
296 mod = MOD_HELD_GRENADE;
297 else if(ent->spawnflags & 1)
298 mod = MOD_HG_SPLASH;
299 else
300 mod = MOD_G_SPLASH;
301 T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod);
302
303 VectorMA(ent->s.origin, -0.02, ent->velocity, origin);
304 gi.WriteByte(svc_temp_entity);
305 if(ent->waterlevel){
306 if(ent->groundentity)
307 gi.WriteByte(TE_GRENADE_EXPLOSION_WATER);
308 else
309 gi.WriteByte(TE_ROCKET_EXPLOSION_WATER);
310 } else {
311 if(ent->groundentity)
312 gi.WriteByte(TE_GRENADE_EXPLOSION);
313 else
314 gi.WriteByte(TE_ROCKET_EXPLOSION);
315 }
316 gi.WritePosition(origin);
317 gi.multicast(ent->s.origin, MULTICAST_PHS);
318
319 G_FreeEdict(ent);
320 }
321
Grenade_Touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)322 static void Grenade_Touch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf){
323 if(other == ent->owner)
324 return;
325
326 if(surf &&(surf->flags & SURF_SKY)){
327 G_FreeEdict(ent);
328 return;
329 }
330
331 if(!other->takedamage){
332 if(ent->spawnflags & 1){
333 if(random() > 0.5)
334 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
335 else
336 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
337 } else {
338 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
339 }
340 return;
341 }
342
343 ent->enemy = other;
344 Grenade_Explode(ent);
345 }
346
347 /* gren launcher */
fire_grenade(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int speed,float timer,float damage_radius)348 void fire_grenade(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius){
349 edict_t *grenade;
350 vec3_t dir;
351 vec3_t forward, right, up;
352
353 vectoangles(aimdir, dir);
354 AngleVectors(dir, forward, right, up);
355
356 grenade = G_Spawn();
357 VectorCopy(start, grenade->s.origin);
358 VectorScale(aimdir, speed, grenade->velocity);
359 VectorMA(grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
360 VectorMA(grenade->velocity, crandom() * 10.0, right, grenade->velocity);
361 VectorSet(grenade->avelocity, 300, 300, 300);
362 grenade->movetype = MOVETYPE_BOUNCE;
363 grenade->clipmask = MASK_SHOT;
364 grenade->solid = SOLID_BBOX;
365 grenade->s.effects |= EF_GRENADE;
366 VectorClear(grenade->mins);
367 VectorClear(grenade->maxs);
368 grenade->s.modelindex = gi.modelindex("models/objects/grenade/tris.md2");
369 grenade->owner = self;
370 grenade->touch = Grenade_Touch;
371 grenade->nextthink = level.time + timer;
372 grenade->think = Grenade_Explode;
373 grenade->dmg = damage;
374 grenade->dmg_radius = damage_radius;
375 grenade->classname = "grenade";
376
377 gi.linkentity(grenade);
378 }
379
380 /* hand grenade */
fire_grenade2(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int speed,float timer,float damage_radius,qboolean held)381 void fire_grenade2(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held){
382 edict_t *grenade;
383 vec3_t dir;
384 vec3_t forward, right, up;
385
386 vectoangles(aimdir, dir);
387 AngleVectors(dir, forward, right, up);
388
389 grenade = G_Spawn();
390 VectorCopy(start, grenade->s.origin);
391 VectorScale(aimdir, speed, grenade->velocity);
392 VectorMA(grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
393 VectorMA(grenade->velocity, crandom() * 10.0, right, grenade->velocity);
394 VectorSet(grenade->avelocity, 300, 300, 300);
395 grenade->movetype = MOVETYPE_BOUNCE;
396 grenade->clipmask = MASK_SHOT;
397 grenade->solid = SOLID_BBOX;
398 grenade->s.effects |= EF_GRENADE;
399 VectorClear(grenade->mins);
400 VectorClear(grenade->maxs);
401 grenade->s.modelindex = gi.modelindex("models/objects/grenade2/tris.md2");
402 grenade->owner = self;
403 grenade->touch = Grenade_Touch;
404 grenade->nextthink = level.time + timer;
405 grenade->think = Grenade_Explode;
406 grenade->dmg = damage;
407 grenade->dmg_radius = damage_radius;
408 grenade->classname = "hgrenade";
409 if(held)
410 grenade->spawnflags = 3;
411 else
412 grenade->spawnflags = 1;
413 grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
414
415 if(timer <= 0.0)
416 Grenade_Explode(grenade);
417 else {
418 gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
419 gi.linkentity(grenade);
420 }
421 }
422
423
424 /*
425 fire_rocket
426 */
rocket_touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)427 void rocket_touch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf){
428 vec3_t origin;
429
430 if(other == ent->owner)
431 return;
432
433 if(surf &&(surf->flags & SURF_SKY)){
434 G_FreeEdict(ent);
435 return;
436 }
437
438 if(ent->owner->client)
439 PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
440
441 // calculate position for the explosion entity
442 VectorMA(ent->s.origin, -0.02, ent->velocity, origin);
443
444 if(other->takedamage)
445 T_Damage(other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);
446
447 T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);
448
449 gi.WriteByte(svc_temp_entity);
450 if(ent->waterlevel)
451 gi.WriteByte(TE_ROCKET_EXPLOSION_WATER);
452 else
453 gi.WriteByte(TE_ROCKET_EXPLOSION);
454 gi.WritePosition(origin);
455 gi.multicast(ent->s.origin, MULTICAST_PHS);
456
457 G_FreeEdict(ent);
458 }
459
fire_rocket(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,float damage_radius,int radius_damage)460 void fire_rocket(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage){
461 edict_t *rocket;
462
463 rocket = G_Spawn();
464 VectorCopy(start, rocket->s.origin);
465 VectorCopy(dir, rocket->movedir);
466 vectoangles(dir, rocket->s.angles);
467 VectorScale(dir, speed, rocket->velocity);
468 rocket->movetype = MOVETYPE_FLYMISSILE;
469 rocket->clipmask = MASK_SHOT;
470 rocket->solid = SOLID_BBOX;
471 rocket->s.effects |= EF_ROCKET;
472 VectorClear(rocket->mins);
473 VectorClear(rocket->maxs);
474 rocket->s.modelindex = gi.modelindex("models/objects/rocket/tris.md2");
475 rocket->owner = self;
476 rocket->touch = rocket_touch;
477 rocket->nextthink = level.time + 8000 / speed;
478 rocket->think = G_FreeEdict;
479 rocket->dmg = damage;
480 rocket->radius_dmg = radius_damage;
481 rocket->dmg_radius = damage_radius;
482 rocket->s.sound = gi.soundindex("weapons/rockfly.wav");
483 rocket->classname = "rocket";
484 rocket->s.renderfx = RF_NOSHADOW;
485
486 gi.linkentity(rocket);
487 }
488
489
490 /*
491 fire_rail
492 */
fire_rail(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int kick)493 void fire_rail(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick){
494 vec3_t from;
495 vec3_t end;
496 trace_t tr;
497 edict_t *ignore;
498 int mask;
499 qboolean water;
500
501 VectorMA(start, 8192, aimdir, end);
502 VectorCopy(start, from);
503 ignore = self;
504 water = false;
505 mask = MASK_SHOT | CONTENTS_SLIME | CONTENTS_LAVA;
506 while(ignore){
507 tr = gi.trace(from, NULL, NULL, end, ignore, mask);
508
509 if(tr.contents &(CONTENTS_SLIME | CONTENTS_LAVA)){
510 mask &= ~(CONTENTS_SLIME | CONTENTS_LAVA);
511 water = true;
512 } else {
513 if((tr.ent->svflags & SVF_MONSTER) ||(tr.ent->client) ||
514 (tr.ent->solid == SOLID_BBOX))
515 ignore = tr.ent;
516 else
517 ignore = NULL;
518
519 if((tr.ent != self) && (tr.ent->takedamage)){
520 if(tr.ent->client && ((int)gameplay->value & INSTAGIB))
521 damage = 999; // cause a really nasty gib :)
522 T_Damage(tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);
523 }
524 }
525
526 VectorCopy(tr.endpos, from);
527 }
528
529 // send gun puff / flash
530 gi.WriteByte(svc_temp_entity);
531 gi.WriteByte(TE_RAILTRAIL);
532 gi.WritePosition(start);
533 gi.WritePosition(tr.endpos);
534 gi.multicast(self->s.origin, MULTICAST_PHS);
535 // gi.multicast(start, MULTICAST_PHS);
536 if(water){
537 gi.WriteByte(svc_temp_entity);
538 gi.WriteByte(TE_RAILTRAIL);
539 gi.WritePosition(start);
540 gi.WritePosition(tr.endpos);
541 gi.multicast(tr.endpos, MULTICAST_PHS);
542 }
543
544 if(self->client)
545 PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
546 }
547
548
549 /*
550 fire_bfg
551 */
bfg_explode(edict_t * self)552 void bfg_explode(edict_t *self){
553 edict_t *ent;
554 float points;
555 vec3_t v;
556 float dist;
557
558 if(self->s.frame == 0){
559 // the BFG effect
560 ent = NULL;
561 while((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL){
562 if(!ent->takedamage)
563 continue;
564 if(ent == self->owner)
565 continue;
566 if(!CanDamage(ent, self))
567 continue;
568 if(!CanDamage(ent, self->owner))
569 continue;
570
571 VectorAdd(ent->mins, ent->maxs, v);
572 VectorMA(ent->s.origin, 0.5, v, v);
573 VectorSubtract(self->s.origin, v, v);
574 dist = VectorLength(v);
575 points = self->radius_dmg *(1.0 - sqrt(dist / self->dmg_radius));
576 if(ent == self->owner)
577 points = points * 0.5;
578
579 gi.WriteByte(svc_temp_entity);
580 gi.WriteByte(TE_BFG_EXPLOSION);
581 gi.WritePosition(ent->s.origin);
582 gi.multicast(ent->s.origin, MULTICAST_PHS);
583 T_Damage(ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin,(int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT);
584 }
585 }
586
587 self->nextthink = level.time + FRAMETIME;
588 self->s.frame++;
589 if(self->s.frame == 5)
590 self->think = G_FreeEdict;
591 }
592
bfg_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)593 void bfg_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
594 if(other == self->owner)
595 return;
596
597 if(surf &&(surf->flags & SURF_SKY)){
598 G_FreeEdict(self);
599 return;
600 }
601
602 if(self->owner->client)
603 PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
604
605 // core explosion - prevents firing it into the wall/floor
606 if(other->takedamage)
607 T_Damage(other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST);
608 T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST);
609
610 gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0);
611 self->solid = SOLID_NOT;
612 self->touch = NULL;
613 VectorMA(self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin);
614 VectorClear(self->velocity);
615 self->s.modelindex = gi.modelindex("sprites/s_bfg3.sp2");
616 self->s.frame = 0;
617 self->s.sound = 0;
618 self->s.effects &= ~EF_ANIM_ALLFAST;
619 self->think = bfg_explode;
620 self->nextthink = level.time + FRAMETIME;
621 self->enemy = other;
622
623 gi.WriteByte(svc_temp_entity);
624 gi.WriteByte(TE_BFG_BIGEXPLOSION);
625 gi.WritePosition(self->s.origin);
626 gi.multicast(self->s.origin, MULTICAST_PVS);
627 }
628
629
bfg_think(edict_t * self)630 void bfg_think(edict_t *self){
631 edict_t *ent;
632 edict_t *ignore;
633 vec3_t point;
634 vec3_t dir;
635 vec3_t start;
636 vec3_t end;
637 int dmg;
638 trace_t tr;
639
640 dmg = 5;
641
642 ent = NULL;
643 while((ent = findradius(ent, self->s.origin, 256)) != NULL){
644 if(ent == self)
645 continue;
646
647 if(ent == self->owner)
648 continue;
649
650 if(!ent->takedamage)
651 continue;
652
653 if(!(ent->svflags & SVF_MONSTER) &&(!ent->client) &&(strcmp(ent->classname, "misc_explobox") != 0))
654 continue;
655
656 VectorMA(ent->absmin, 0.5, ent->size, point);
657
658 VectorSubtract(point, self->s.origin, dir);
659 VectorNormalize(dir);
660
661 ignore = self;
662 VectorCopy(self->s.origin, start);
663 VectorMA(start, 2048, dir, end);
664 while(1){
665 tr = gi.trace(start, NULL, NULL, end, ignore, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER);
666
667 if(!tr.ent)
668 break;
669
670 // hurt it if we can
671 if((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) &&(tr.ent != self->owner))
672 T_Damage(tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);
673
674 // if we hit something that's not a monster or player we're done
675 if(!(tr.ent->svflags & SVF_MONSTER) &&(!tr.ent->client)){
676 gi.WriteByte(svc_temp_entity);
677 gi.WriteByte(TE_LASER_SPARKS);
678 gi.WriteByte(4);
679 gi.WritePosition(tr.endpos);
680 gi.WriteDir(tr.plane.normal);
681 gi.WriteByte(self->s.skinnum);
682 gi.multicast(tr.endpos, MULTICAST_PVS);
683 break;
684 }
685
686 ignore = tr.ent;
687 VectorCopy(tr.endpos, start);
688 }
689
690 gi.WriteByte(svc_temp_entity);
691 gi.WriteByte(TE_BFG_LASER);
692 gi.WritePosition(self->s.origin);
693 gi.WritePosition(tr.endpos);
694 gi.multicast(self->s.origin, MULTICAST_PHS);
695 }
696
697 self->nextthink = level.time + FRAMETIME;
698 }
699
700
fire_bfg(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,float damage_radius)701 void fire_bfg(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius){
702 edict_t *bfg;
703
704 bfg = G_Spawn();
705 VectorCopy(start, bfg->s.origin);
706 VectorCopy(dir, bfg->movedir);
707 vectoangles(dir, bfg->s.angles);
708 VectorScale(dir, speed, bfg->velocity);
709 bfg->movetype = MOVETYPE_FLYMISSILE;
710 bfg->clipmask = MASK_SHOT;
711 bfg->solid = SOLID_BBOX;
712 bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST;
713 VectorClear(bfg->mins);
714 VectorClear(bfg->maxs);
715 bfg->s.modelindex = gi.modelindex("sprites/s_bfg1.sp2");
716 bfg->owner = self;
717 bfg->touch = bfg_touch;
718 bfg->nextthink = level.time + 8000 / speed;
719 bfg->think = G_FreeEdict;
720 bfg->radius_dmg = damage;
721 bfg->dmg_radius = damage_radius;
722 bfg->classname = "bfg blast";
723 bfg->s.sound = gi.soundindex("weapons/bfg__l1a.wav");
724
725 bfg->think = bfg_think;
726 bfg->nextthink = level.time + FRAMETIME;
727 bfg->teammaster = bfg;
728 bfg->teamchain = NULL;
729
730 gi.linkentity(bfg);
731 }
732