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