1 #include "g_local.h"
2 
3 #define INCLUDE_ETF_RIFLE		1
4 #define INCLUDE_PROX			1
5 //#define INCLUDE_FLAMETHROWER	1
6 //#define INCLUDE_INCENDIARY		1
7 #define INCLUDE_NUKE			1
8 #define INCLUDE_MELEE			1
9 #define INCLUDE_TESLA			1
10 #define INCLUDE_BEAMS			1
11 
12 extern void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed);
13 extern void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
14 extern void droptofloor (edict_t *ent);
15 extern void Grenade_Explode (edict_t *ent);
16 
17 extern void drawbbox (edict_t *ent);
18 
19 #ifdef INCLUDE_ETF_RIFLE
20 /*
21 ========================
22 fire_flechette
23 ========================
24 */
flechette_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)25 void flechette_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
26 {
27 	vec3_t		dir;
28 
29 	if (other == self->owner)
30 		return;
31 
32 	if (surf && (surf->flags & SURF_SKY))
33 	{
34 		G_FreeEdict (self);
35 		return;
36 	}
37 
38 	if (self->client)
39 		PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
40 
41 	if (other->takedamage)
42 	{
43 //gi.dprintf("t_damage %s\n", other->classname);
44 		T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
45 			self->dmg, self->dmg_radius, DAMAGE_NO_REG_ARMOR, MOD_ETF_RIFLE);
46 	}
47 	else
48 	{
49 		if(!plane)
50 			VectorClear (dir);
51 		else
52 			VectorScale (plane->normal, 256, dir);
53 		gi.WriteByte (svc_temp_entity);
54 		gi.WriteByte (TE_FLECHETTE);
55 		gi.WritePosition (self->s.origin);
56 		gi.WriteDir (dir);
57 		gi.multicast (self->s.origin, MULTICAST_PVS);
58 
59 //		T_RadiusDamage(self, self->owner, 24, self, 48, MOD_ETF_RIFLE);
60 	}
61 
62 	G_FreeEdict (self);
63 }
64 
fire_flechette(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,int kick)65 void fire_flechette (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int kick)
66 {
67 	edict_t *flechette;
68 
69 	VectorNormalize (dir);
70 
71 	flechette = G_Spawn();
72 	VectorCopy (start, flechette->s.origin);
73 	VectorCopy (start, flechette->s.old_origin);
74 	vectoangles2 (dir, flechette->s.angles);
75 
76 	VectorScale (dir, speed, flechette->velocity);
77 	flechette->movetype = MOVETYPE_FLYMISSILE;
78 	flechette->clipmask = MASK_SHOT;
79 	flechette->solid = SOLID_BBOX;
80 	flechette->s.renderfx = RF_FULLBRIGHT;
81 	VectorClear (flechette->mins);
82 	VectorClear (flechette->maxs);
83 
84 	flechette->s.modelindex = gi.modelindex ("models/proj/flechette/tris.md2");
85 
86 //	flechette->s.sound = gi.soundindex ("");			// FIXME - correct sound!
87 	flechette->owner = self;
88 	flechette->touch = flechette_touch;
89 	flechette->nextthink = level.time + 8000/speed;
90 	flechette->think = G_FreeEdict;
91 	flechette->dmg = damage;
92 	flechette->dmg_radius = kick;
93 
94 	gi.linkentity (flechette);
95 
96 	if (self->client)
97 		check_dodge (self, flechette->s.origin, dir, speed);
98 }
99 #endif
100 
101 // **************************
102 // PROX
103 // **************************
104 
105 #ifdef INCLUDE_PROX
106 #define PROX_TIME_TO_LIVE	45		// 45, 30, 15, 10
107 #define PROX_TIME_DELAY		0.5
108 #define PROX_BOUND_SIZE		96
109 #define PROX_DAMAGE_RADIUS	192
110 #define PROX_HEALTH			20
111 #define	PROX_DAMAGE			90
112 
113 //===============
114 //===============
Prox_Explode(edict_t * ent)115 void Prox_Explode (edict_t *ent)
116 {
117 	vec3_t		origin;
118 	edict_t		*owner;
119 
120 // free the trigger field
121 
122 	//PMM - changed teammaster to "mover" .. owner of the field is the prox
123 	if(ent->teamchain && ent->teamchain->owner == ent)
124 		G_FreeEdict(ent->teamchain);
125 
126 	owner = ent;
127 	if(ent->teammaster)
128 	{
129 		owner = ent->teammaster;
130 		PlayerNoise(owner, ent->s.origin, PNOISE_IMPACT);
131 	}
132 
133 	// play quad sound if appopriate
134 	if (ent->dmg > PROX_DAMAGE)
135 		gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
136 
137 	ent->takedamage = DAMAGE_NO;
138 	T_RadiusDamage(ent, owner, ent->dmg, ent, PROX_DAMAGE_RADIUS, MOD_PROX);
139 
140 	VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
141 	gi.WriteByte (svc_temp_entity);
142 	if (ent->groundentity)
143 		gi.WriteByte (TE_GRENADE_EXPLOSION);
144 	else
145 		gi.WriteByte (TE_ROCKET_EXPLOSION);
146 	gi.WritePosition (origin);
147 	gi.multicast (ent->s.origin, MULTICAST_PVS);
148 
149 	G_FreeEdict (ent);
150 }
151 
152 //===============
153 //===============
prox_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)154 void prox_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
155 {
156 //	gi.dprintf("prox_die\n");
157 	// if set off by another prox, delay a little (chained explosions)
158 	if (strcmp(inflictor->classname, "prox"))
159 	{
160 		self->takedamage = DAMAGE_NO;
161 		Prox_Explode(self);
162 	}
163 	else
164 	{
165 		self->takedamage = DAMAGE_NO;
166 		self->think = Prox_Explode;
167 		self->nextthink = level.time + FRAMETIME;
168 	}
169 }
170 
171 //===============
172 //===============
Prox_Field_Touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)173 void Prox_Field_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
174 {
175 	edict_t *prox;
176 
177 	if (!(other->svflags & SVF_MONSTER) && !other->client)
178 		return;
179 
180 	// trigger the prox mine if it's still there, and still mine.
181 	prox = ent->owner;
182 
183 	if (other == prox) // don't set self off
184 		return;
185 
186 	if (prox->think == Prox_Explode) // we're set to blow!
187 	{
188 //		if ((g_showlogic) && (g_showlogic->value))
189 //			gi.dprintf ("%f - prox already gone off!\n", level.time);
190 		return;
191 	}
192 
193 	if(prox->teamchain == ent)
194 	{
195 		gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxwarn.wav"), 1, ATTN_NORM, 0);
196 		prox->think = Prox_Explode;
197 		prox->nextthink = level.time + PROX_TIME_DELAY;
198 		return;
199 	}
200 
201 	ent->solid = SOLID_NOT;
202 	G_FreeEdict(ent);
203 }
204 
205 //===============
206 //===============
prox_seek(edict_t * ent)207 void prox_seek (edict_t *ent)
208 {
209 	if(level.time > ent->wait)
210 	{
211 		Prox_Explode(ent);
212 	}
213 	else
214 	{
215 		ent->s.frame++;
216 		if(ent->s.frame > 13)
217 			ent->s.frame = 9;
218 		ent->think = prox_seek;
219 		ent->nextthink = level.time + 0.1;
220 	}
221 }
222 
223 //===============
224 //===============
prox_open(edict_t * ent)225 void prox_open (edict_t *ent)
226 {
227 	edict_t *search;
228 
229 	search = NULL;
230 //	gi.dprintf("prox_open %d\n", ent->s.frame);
231 //	gi.dprintf("%f\n", ent->velocity[2]);
232 	if(ent->s.frame == 9)	// end of opening animation
233 	{
234 		// set the owner to NULL so the owner can shoot it, etc.  needs to be done here so the owner
235 		// doesn't get stuck on it while it's opening if fired at point blank wall
236 		ent->s.sound = 0;
237 		ent->owner = NULL;
238 		if(ent->teamchain)
239 			ent->teamchain->touch = Prox_Field_Touch;
240 		while ((search = findradius(search, ent->s.origin, PROX_DAMAGE_RADIUS+10)) != NULL)
241 		{
242 			if (!search->classname)			// tag token and other weird shit
243 				continue;
244 
245 //			if (!search->takedamage)
246 //				continue;
247 			// if it's a monster or player with health > 0
248 			// or it's a player start point
249 			// and we can see it
250 			// blow up
251 			if (
252 				(
253 					(((search->svflags & SVF_MONSTER) || (search->client)) && (search->health > 0))	||
254 					(
255 						(deathmatch->value) &&
256 						(
257 						(!strcmp(search->classname, "info_player_deathmatch")) ||
258 						(!strcmp(search->classname, "info_player_start")) ||
259 						(!strcmp(search->classname, "info_player_coop")) ||
260 						(!strcmp(search->classname, "misc_teleporter_dest"))
261 						)
262 					)
263 				)
264 				&& (visible (search, ent))
265 			   )
266 			{
267 				gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxwarn.wav"), 1, ATTN_NORM, 0);
268 				Prox_Explode (ent);
269 				return;
270 			}
271 		}
272 
273 		if (strong_mines && (strong_mines->value))
274 			ent->wait = level.time + PROX_TIME_TO_LIVE;
275 		else
276 		{
277 			switch (ent->dmg/PROX_DAMAGE)
278 			{
279 				case 1:
280 					ent->wait = level.time + PROX_TIME_TO_LIVE;
281 					break;
282 				case 2:
283 					ent->wait = level.time + 30;
284 					break;
285 				case 4:
286 					ent->wait = level.time + 15;
287 					break;
288 				case 8:
289 					ent->wait = level.time + 10;
290 					break;
291 				default:
292 //					if ((g_showlogic) && (g_showlogic->value))
293 //						gi.dprintf ("prox with unknown multiplier %d!\n", ent->dmg/PROX_DAMAGE);
294 					ent->wait = level.time + PROX_TIME_TO_LIVE;
295 					break;
296 			}
297 		}
298 
299 //		ent->wait = level.time + PROX_TIME_TO_LIVE;
300 		ent->think = prox_seek;
301 		ent->nextthink = level.time + 0.2;
302 	}
303 	else
304 	{
305 		if (ent->s.frame == 0)
306 			gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxopen.wav"), 1, ATTN_NORM, 0);
307 		//ent->s.sound = gi.soundindex ("weapons/proxopen.wav");
308 		ent->s.frame++;
309 		ent->think = prox_open;
310 		ent->nextthink = level.time + 0.05;
311 	}
312 }
313 
314 //===============
315 //===============
prox_land(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)316 void prox_land (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
317 {
318 	edict_t	*field;
319 	vec3_t	dir;
320 	vec3_t	forward, right, up;
321 	int		makeslave = 0;
322 	int		movetype = MOVETYPE_NONE;
323 	int		stick_ok = 0;
324 	vec3_t	land_point;
325 
326 	// must turn off owner so owner can shoot it and set it off
327 	// moved to prox_open so owner can get away from it if fired at pointblank range into
328 	// wall
329 //	ent->owner = NULL;
330 
331 //	if ((g_showlogic) && (g_showlogic->value))
332 //		gi.dprintf ("land - %2.2f %2.2f %2.2f\n", ent->velocity[0], ent->velocity[1], ent->velocity[2]);
333 
334 	if (surf && (surf->flags & SURF_SKY))
335 	{
336 		G_FreeEdict(ent);
337 		return;
338 	}
339 
340 	if (plane->normal)
341 	{
342 		VectorMA (ent->s.origin, -10.0, plane->normal, land_point);
343 		if (gi.pointcontents (land_point) & (CONTENTS_SLIME|CONTENTS_LAVA))
344 		{
345 			Prox_Explode (ent);
346 			return;
347 		}
348 	}
349 
350 	if ((other->svflags & SVF_MONSTER) || other->client || (other->svflags & SVF_DAMAGEABLE))
351 	{
352 		if(other != ent->teammaster)
353 			Prox_Explode(ent);
354 
355 		return;
356 	}
357 
358 #define STOP_EPSILON	0.1
359 
360 	else if (other != world)
361 	{
362 		//Here we need to check to see if we can stop on this entity.
363 		//Note that plane can be NULL
364 
365 		//PMM - code stolen from g_phys (ClipVelocity)
366 		vec3_t out;
367 		float backoff, change;
368 		int i;
369 
370 		if (!plane->normal) // this happens if you hit a point object, maybe other cases
371 		{
372 			// Since we can't tell what's going to happen, just blow up
373 //			if ((g_showlogic) && (g_showlogic->value))
374 //				gi.dprintf ("bad normal for surface, exploding!\n");
375 
376 			Prox_Explode(ent);
377 			return;
378 		}
379 
380 		if ((other->movetype == MOVETYPE_PUSH) && (plane->normal[2] > 0.7))
381 			stick_ok = 1;
382 		else
383 			stick_ok = 0;
384 
385 		backoff = DotProduct (ent->velocity, plane->normal) * 1.5;
386 		for (i=0 ; i<3 ; i++)
387 		{
388 			change = plane->normal[i]*backoff;
389 			out[i] = ent->velocity[i] - change;
390 			if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
391 				out[i] = 0;
392 		}
393 
394 		if (out[2] > 60)
395 			return;
396 
397 		movetype = MOVETYPE_BOUNCE;
398 
399 		// if we're here, we're going to stop on an entity
400 		if (stick_ok)
401 		{ // it's a happy entity
402 			VectorCopy (vec3_origin, ent->velocity);
403 			VectorCopy (vec3_origin, ent->avelocity);
404 		}
405 		else // no-stick.  teflon time
406 		{
407 			if (plane->normal[2] > 0.7)
408 			{
409 //				if ((g_showlogic) && (g_showlogic->value))
410 //					gi.dprintf ("stuck on entity, blowing up!\n");
411 
412 				Prox_Explode(ent);
413 				return;
414 			}
415 			return;
416 		}
417 	}
418 	else if (other->s.modelindex != 1)
419 		return;
420 
421 	vectoangles2 (plane->normal, dir);
422 	AngleVectors (dir, forward, right, up);
423 
424 	if (gi.pointcontents (ent->s.origin) & (CONTENTS_LAVA|CONTENTS_SLIME))
425 	{
426 		Prox_Explode (ent);
427 		return;
428 	}
429 
430 	field = G_Spawn();
431 
432 	VectorCopy (ent->s.origin, field->s.origin);
433 	VectorClear(field->velocity);
434 	VectorClear(field->avelocity);
435 	VectorSet(field->mins, -PROX_BOUND_SIZE, -PROX_BOUND_SIZE, -PROX_BOUND_SIZE);
436 	VectorSet(field->maxs, PROX_BOUND_SIZE, PROX_BOUND_SIZE, PROX_BOUND_SIZE);
437 	field->movetype = MOVETYPE_NONE;
438 	field->solid = SOLID_TRIGGER;
439 	field->owner = ent;
440 	field->classname = "prox_field";
441 	field->teammaster = ent;
442 	gi.linkentity (field);
443 
444 	VectorClear(ent->velocity);
445 	VectorClear(ent->avelocity);
446 	// rotate to vertical
447 	dir[PITCH] = dir[PITCH] + 90;
448 	VectorCopy (dir, ent->s.angles);
449 	ent->takedamage = DAMAGE_AIM;
450 	ent->movetype = movetype;		// either bounce or none, depending on whether we stuck to something
451 	ent->die = prox_die;
452 	ent->teamchain = field;
453 	ent->health = PROX_HEALTH;
454 	ent->nextthink = level.time + 0.05;
455 	ent->think = prox_open;
456 	ent->touch = NULL;
457 	ent->solid = SOLID_BBOX;
458 	// record who we're attached to
459 //	ent->teammaster = other;
460 
461 	gi.linkentity(ent);
462 }
463 
464 //===============
465 //===============
fire_prox(edict_t * self,vec3_t start,vec3_t aimdir,int damage_multiplier,int speed)466 void fire_prox (edict_t *self, vec3_t start, vec3_t aimdir, int damage_multiplier, int speed)
467 {
468 	edict_t	*prox;
469 	vec3_t	dir;
470 	vec3_t	forward, right, up;
471 
472 	vectoangles2 (aimdir, dir);
473 	AngleVectors (dir, forward, right, up);
474 
475 //	if ((g_showlogic) && (g_showlogic->value))
476 //		gi.dprintf ("start %s    aim %s   speed %d\n", vtos(start), vtos(aimdir), speed);
477 	prox = G_Spawn();
478 	VectorCopy (start, prox->s.origin);
479 	VectorScale (aimdir, speed, prox->velocity);
480 	VectorMA (prox->velocity, 200 + crandom() * 10.0, up, prox->velocity);
481 	VectorMA (prox->velocity, crandom() * 10.0, right, prox->velocity);
482 	VectorCopy (dir, prox->s.angles);
483 	prox->s.angles[PITCH]-=90;
484 	prox->movetype = MOVETYPE_BOUNCE;
485 	prox->solid = SOLID_BBOX;
486 	prox->s.effects |= EF_GRENADE;
487 	prox->clipmask = MASK_SHOT|CONTENTS_LAVA|CONTENTS_SLIME;
488 	prox->s.renderfx |= RF_IR_VISIBLE;
489 	//FIXME - this needs to be bigger.  Has other effects, though.  Maybe have to change origin to compensate
490 	// so it sinks in correctly.  Also in lavacheck, might have to up the distance
491 	VectorSet (prox->mins, -6, -6, -6);
492 	VectorSet (prox->maxs, 6, 6, 6);
493 	prox->s.modelindex = gi.modelindex ("models/weapons/g_prox/tris.md2");
494 	prox->owner = self;
495 	prox->teammaster = self;
496 	prox->touch = prox_land;
497 //	prox->nextthink = level.time + PROX_TIME_TO_LIVE;
498 	prox->think = Prox_Explode;
499 	prox->dmg = PROX_DAMAGE*damage_multiplier;
500 	prox->classname = "prox";
501 	prox->svflags |= SVF_DAMAGEABLE;
502 	prox->flags |= FL_MECHANICAL;
503 
504 	switch (damage_multiplier)
505 	{
506 	case 1:
507 		prox->nextthink = level.time + PROX_TIME_TO_LIVE;
508 		break;
509 	case 2:
510 		prox->nextthink = level.time + 30;
511 		break;
512 	case 4:
513 		prox->nextthink = level.time + 15;
514 		break;
515 	case 8:
516 		prox->nextthink = level.time + 10;
517 		break;
518 	default:
519 //		if ((g_showlogic) && (g_showlogic->value))
520 //			gi.dprintf ("prox with unknown multiplier %d!\n", damage_multiplier);
521 		prox->nextthink = level.time + PROX_TIME_TO_LIVE;
522 		break;
523 	}
524 
525 	gi.linkentity (prox);
526 }
527 #endif
528 
529 // *************************
530 // FLAMETHROWER
531 // *************************
532 
533 #ifdef INCLUDE_FLAMETHROWER
534 #define FLAMETHROWER_RADIUS		8
535 
fire_remove(edict_t * ent)536 void fire_remove (edict_t *ent)
537 {
538 	if(ent == ent->owner->teamchain)
539 		ent->owner->teamchain = NULL;
540 
541 	G_FreeEdict(ent);
542 }
543 
fire_flame(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int speed)544 void fire_flame (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)
545 {
546 	edict_t *flame;
547 	vec3_t	dir;
548 	vec3_t	forward, right, up;
549 
550 	vectoangles2 (aimdir, dir);
551 	AngleVectors (dir, forward, right, up);
552 
553 	flame = G_Spawn();
554 
555 	// the origin is the first control point, put it speed forward.
556 	VectorMA(start, speed, forward, flame->s.origin);
557 
558 	// record that velocity
559 	VectorScale (aimdir, speed, flame->velocity);
560 
561 	VectorCopy (dir, flame->s.angles);
562 	flame->movetype = MOVETYPE_NONE;
563 	flame->solid = SOLID_NOT;
564 
565 	VectorSet(flame->mins, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS);
566 	VectorSet(flame->maxs, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS);
567 
568 	flame->s.sound = gi.soundindex ("weapons/flame.wav");
569 	flame->owner = self;
570 	flame->dmg = damage;
571 	flame->classname = "flame";
572 
573 	// clear control points and velocities
574 	VectorCopy (flame->s.origin, flame->flameinfo.pos1);
575 	VectorCopy (flame->velocity, flame->flameinfo.vel1);
576 	VectorCopy (flame->s.origin, flame->flameinfo.pos2);
577 	VectorCopy (flame->velocity, flame->flameinfo.vel2);
578 	VectorCopy (flame->s.origin, flame->flameinfo.pos3);
579 	VectorCopy (flame->velocity, flame->flameinfo.vel3);
580 	VectorCopy (flame->s.origin, flame->flameinfo.pos4);
581 
582 	// hook flame stream to owner
583 	self->teamchain = flame;
584 
585 	gi.linkentity (flame);
586 }
587 
588 // fixme - change to use start location, not entity origin
fire_maintain(edict_t * ent,edict_t * flame,vec3_t start,vec3_t aimdir,int damage,int speed)589 void fire_maintain (edict_t *ent, edict_t *flame, vec3_t start, vec3_t aimdir, int damage, int speed)
590 {
591 	trace_t	tr;
592 
593 	// move the control points out the appropriate direction and velocity
594 	VectorAdd(flame->flameinfo.pos3, flame->flameinfo.vel3, flame->flameinfo.pos4);
595 	VectorAdd(flame->flameinfo.pos2, flame->flameinfo.vel2, flame->flameinfo.pos3);
596 	VectorAdd(flame->flameinfo.pos1, flame->flameinfo.vel1, flame->flameinfo.pos2);
597 	VectorAdd(flame->s.origin,		 flame->velocity,       flame->flameinfo.pos1);
598 
599 	// move the velocities for the control points
600 	VectorCopy(flame->flameinfo.vel2, flame->flameinfo.vel3);
601 	VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel2);
602 	VectorCopy(flame->velocity,		  flame->flameinfo.vel1);
603 
604 	// set velocity and location for new control point 0.
605 	VectorMA(start, speed, aimdir, flame->s.origin);
606 	VectorScale(aimdir, speed, flame->velocity);
607 
608 	//
609 	// does it hit a wall? if so, when?
610 	//
611 
612 	// player fire point to flame origin.
613 	tr = gi.trace(start, flame->mins, flame->maxs,
614 					flame->s.origin, flame, MASK_SHOT);
615 	if(tr.fraction == 1.0)
616 	{
617 		// origin to point 1
618 		tr = gi.trace(flame->s.origin, flame->mins, flame->maxs,
619 						flame->flameinfo.pos1, flame, MASK_SHOT);
620 		if(tr.fraction == 1.0)
621 		{
622 			// point 1 to point 2
623 			tr = gi.trace(flame->flameinfo.pos1, flame->mins, flame->maxs,
624 							flame->flameinfo.pos2, flame, MASK_SHOT);
625 			if(tr.fraction == 1.0)
626 			{
627 				// point 2 to point 3
628 				tr = gi.trace(flame->flameinfo.pos2, flame->mins, flame->maxs,
629 							flame->flameinfo.pos3, flame, MASK_SHOT);
630 				if(tr.fraction == 1.0)
631 				{
632 					// point 3 to point 4, point 3 valid
633 					tr = gi.trace(flame->flameinfo.pos3, flame->mins, flame->maxs,
634 								flame->flameinfo.pos4, flame, MASK_SHOT);
635 					if(tr.fraction < 1.0) // point 4 blocked
636 					{
637 						VectorCopy(tr.endpos, flame->flameinfo.pos4);
638 					}
639 				}
640 				else	// point 3 blocked, point 2 valid
641 				{
642 					VectorCopy(flame->flameinfo.vel2, flame->flameinfo.vel3);
643 					VectorCopy(tr.endpos, flame->flameinfo.pos3);
644 					VectorCopy(tr.endpos, flame->flameinfo.pos4);
645 				}
646 			}
647 			else	// point 2 blocked, point 1 valid
648 			{
649 				VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel2);
650 				VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel3);
651 				VectorCopy(tr.endpos, flame->flameinfo.pos2);
652 				VectorCopy(tr.endpos, flame->flameinfo.pos3);
653 				VectorCopy(tr.endpos, flame->flameinfo.pos4);
654 			}
655 		}
656 		else	// point 1 blocked, origin valid
657 		{
658 			VectorCopy(flame->velocity, flame->flameinfo.vel1);
659 			VectorCopy(flame->velocity, flame->flameinfo.vel2);
660 			VectorCopy(flame->velocity, flame->flameinfo.vel3);
661 			VectorCopy(tr.endpos, flame->flameinfo.pos1);
662 			VectorCopy(tr.endpos, flame->flameinfo.pos2);
663 			VectorCopy(tr.endpos, flame->flameinfo.pos3);
664 			VectorCopy(tr.endpos, flame->flameinfo.pos4);
665 		}
666 	}
667 	else // origin blocked!
668 	{
669 //		gi.dprintf("point 2 blocked\n");
670 		VectorCopy(flame->velocity, flame->flameinfo.vel1);
671 		VectorCopy(flame->velocity, flame->flameinfo.vel2);
672 		VectorCopy(flame->velocity, flame->flameinfo.vel3);
673 		VectorCopy(tr.endpos, flame->s.origin);
674 		VectorCopy(tr.endpos, flame->flameinfo.pos1);
675 		VectorCopy(tr.endpos, flame->flameinfo.pos2);
676 		VectorCopy(tr.endpos, flame->flameinfo.pos3);
677 		VectorCopy(tr.endpos, flame->flameinfo.pos4);
678 	}
679 
680 	if(tr.fraction < 1.0 && tr.ent->takedamage)
681 	{
682 		T_Damage (tr.ent, flame, ent, flame->velocity, tr.endpos, tr.plane.normal,
683 					damage, 0, DAMAGE_NO_KNOCKBACK | DAMAGE_ENERGY | DAMAGE_FIRE);
684 	}
685 
686 	gi.linkentity(flame);
687 
688 	gi.WriteByte (svc_temp_entity);
689 	gi.WriteByte (TE_FLAME);
690 	gi.WriteShort(ent - g_edicts);
691 	gi.WriteShort(6);
692 	gi.WritePosition (start);
693 	gi.WritePosition (flame->s.origin);
694 	gi.WritePosition (flame->flameinfo.pos1);
695 	gi.WritePosition (flame->flameinfo.pos2);
696 	gi.WritePosition (flame->flameinfo.pos3);
697 	gi.WritePosition (flame->flameinfo.pos4);
698 	gi.multicast (flame->s.origin, MULTICAST_PVS);
699 }
700 
701 /*QUAKED trap_flameshooter (1 0 0) (-8 -8 -8) (8 8 8)
702 */
703 #define FLAMESHOOTER_VELOCITY			50
704 #define FLAMESHOOTER_DAMAGE				20
705 #define FLAMESHOOTER_BURST_VELOCITY		300
706 #define FLAMESHOOTER_BURST_DAMAGE		30
707 
708 //#define FLAMESHOOTER_PUFF	1
709 #define FLAMESHOOTER_STREAM	1
710 
flameshooter_think(edict_t * self)711 void flameshooter_think (edict_t *self)
712 {
713 	vec3_t	forward, right, up;
714 	edict_t *flame;
715 
716 	if(self->delay)
717 	{
718 		if(self->teamchain)
719 			fire_remove (self->teamchain);
720 		return;
721 	}
722 
723 	self->s.angles[1] += self->speed;
724 	if(self->s.angles[1] > 135 || self->s.angles[1] < 45)
725 		self->speed = -self->speed;
726 
727 	AngleVectors (self->s.angles, forward, right, up);
728 
729 #ifdef FLAMESHOOTER_STREAM
730 	flame = self->teamchain;
731 	if(!self->teamchain)
732 		fire_flame (self, self->s.origin, forward, FLAMESHOOTER_DAMAGE, FLAMESHOOTER_VELOCITY);
733 	else
734 		fire_maintain (self, flame, self->s.origin, forward, FLAMESHOOTER_DAMAGE, FLAMESHOOTER_VELOCITY);
735 
736 	self->think = flameshooter_think;
737 	self->nextthink = level.time + 0.05;
738 #else
739 	fire_burst (self, self->s.origin, forward, FLAMESHOOTER_BURST_DAMAGE, FLAMESHOOTER_BURST_VELOCITY);
740 
741 	self->think = flameshooter_think;
742 	self->nextthink = level.time + 0.1;
743 #endif
744 }
745 
flameshooter_use(edict_t * self,edict_t * other,edict_t * activator)746 void flameshooter_use (edict_t *self, edict_t *other, edict_t *activator)
747 {
748 	if(self->delay)
749 	{
750 		self->delay = 0;
751 		self->think = flameshooter_think;
752 		self->nextthink = level.time + 0.1;
753 	}
754 	else
755 		self->delay = 1;
756 }
757 
SP_trap_flameshooter(edict_t * self)758 void SP_trap_flameshooter(edict_t *self)
759 {
760 	vec3_t	tempAngles;
761 
762 	self->solid = SOLID_NOT;
763 	self->movetype = MOVETYPE_NONE;
764 
765 	self->delay = 0;
766 
767 	self->use =	flameshooter_use;
768 	if(self->delay == 0)
769 	{
770 		self->think = flameshooter_think;
771 		self->nextthink = level.time  + 0.1;
772 	}
773 
774 //	self->flags |= FL_NOCLIENT;
775 
776 	self->speed = 10;
777 
778 //	self->speed = 0;	// FIXME this stops the spraying
779 
780 	VectorCopy(self->s.angles, tempAngles);
781 
782 	if (!VectorCompare(self->s.angles, vec3_origin))
783 		G_SetMovedir (self->s.angles, self->movedir);
784 
785 	VectorCopy(tempAngles, self->s.angles);
786 
787 //	gi.setmodel (self, self->model);
788 	gi.linkentity (self);
789 }
790 
791 // *************************
792 // fire_burst
793 // *************************
794 
795 #define FLAME_BURST_MAX_SIZE	64
796 #define FLAME_BURST_FRAMES		20
797 #define FLAME_BURST_MIDPOINT	10
798 
fire_burst_touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)799 void fire_burst_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
800 {
801 	int		powerunits;
802 	int		damage, radius;
803 	vec3_t	origin;
804 
805 	if (surf && (surf->flags & SURF_SKY))
806 	{
807 //		gi.dprintf("Hit sky. Removed\n");
808 		G_FreeEdict (ent);
809 		return;
810 	}
811 
812 	if(other == ent->owner || ent == other)
813 		return;
814 
815 	// don't let flame puffs blow each other up
816 	if(other->classname && !strcmp(other->classname, ent->classname))
817 		return;
818 
819 	if(ent->waterlevel)
820 	{
821 //		gi.dprintf("Hit water. Removed\n");
822 		G_FreeEdict(ent);
823 	}
824 
825 	if(!(other->svflags & SVF_MONSTER) && !other->client)
826 	{
827 		powerunits = FLAME_BURST_FRAMES - ent->s.frame;
828 		damage = powerunits * 6;
829 		radius = powerunits * 4;
830 
831 //		T_RadiusDamage (inflictor, attacker, damage, ignore, radius)
832 		T_RadiusDamage(ent, ent->owner, damage, ent, radius, DAMAGE_FIRE);
833 
834 //		gi.dprintf("Hit world: %d pts, %d rad\n", damage, radius);
835 
836 		// calculate position for the explosion entity
837 		VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
838 
839 		gi.WriteByte (svc_temp_entity);
840 		gi.WriteByte (TE_PLAIN_EXPLOSION);
841 		gi.WritePosition (origin);
842 		gi.multicast (ent->s.origin, MULTICAST_PVS);
843 
844 		G_FreeEdict (ent);
845 	}
846 }
847 
fire_burst_think(edict_t * self)848 void fire_burst_think (edict_t *self)
849 {
850 	int	current_radius;
851 
852 	if(self->waterlevel)
853 	{
854 		G_FreeEdict(self);
855 		return;
856 	}
857 
858 	self->s.frame++;
859 	if(self->s.frame >= FLAME_BURST_FRAMES)
860 	{
861 		G_FreeEdict(self);
862 		return;
863 	}
864 
865 	else if(self->s.frame < FLAME_BURST_MIDPOINT)
866 	{
867 		current_radius = (FLAME_BURST_MAX_SIZE / FLAME_BURST_MIDPOINT) * self->s.frame;
868 	}
869 	else
870 	{
871 		current_radius = (FLAME_BURST_MAX_SIZE / FLAME_BURST_MIDPOINT) * (FLAME_BURST_FRAMES - self->s.frame);
872 	}
873 
874 	if(self->s.frame == 3)
875 		self->s.skinnum = 1;
876 	else if (self->s.frame == 7)
877 		self->s.skinnum = 2;
878 	else if (self->s.frame == 10)
879 		self->s.skinnum = 3;
880 	else if (self->s.frame == 13)
881 		self->s.skinnum = 4;
882 	else if (self->s.frame == 16)
883 		self->s.skinnum = 5;
884 	else if (self->s.frame == 19)
885 		self->s.skinnum = 6;
886 
887 	if(current_radius < 8)
888 		current_radius = 8;
889 	else if(current_radius > FLAME_BURST_MAX_SIZE)
890 		current_radius = FLAME_BURST_MAX_SIZE;
891 
892 	T_RadiusDamage(self, self->owner, self->dmg, self, current_radius, DAMAGE_FIRE);
893 
894 	self->think = fire_burst_think;
895 	self->nextthink = level.time + 0.1;
896 }
897 
fire_burst(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int speed)898 void fire_burst (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)
899 {
900 	edict_t *flame;
901 	vec3_t	dir;
902 	vec3_t	baseVel;
903 	vec3_t	forward, right, up;
904 
905 	vectoangles2 (aimdir, dir);
906 	AngleVectors (dir, forward, right, up);
907 
908 	flame = G_Spawn();
909 	VectorCopy(start, flame->s.origin);
910 //	VectorScale (aimdir, speed, flame->velocity);
911 
912 	// scale down so only 30% of player's velocity is taken into account.
913 	VectorScale (self->velocity, 0.3, baseVel);
914 	VectorMA(baseVel, speed, aimdir, flame->velocity);
915 
916 	VectorCopy (dir, flame->s.angles);
917 	flame->movetype = MOVETYPE_FLY;
918 	flame->solid = SOLID_TRIGGER;
919 
920 	VectorSet(flame->mins, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS);
921 	VectorSet(flame->maxs, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS);
922 
923 	flame->s.sound = gi.soundindex ("weapons/flame.wav");
924 	flame->s.modelindex = gi.modelindex ("models/projectiles/puff/tris.md2");
925 	flame->owner = self;
926 	flame->touch = fire_burst_touch;
927 	flame->think = fire_burst_think;
928 	flame->nextthink = level.time + 0.1;
929 	flame->dmg = damage;
930 	flame->classname = "flameburst";
931 	flame->s.effects = EF_FIRE_PUFF;
932 
933 	gi.linkentity (flame);
934 }
935 #endif
936 
937 // *************************
938 //	INCENDIARY GRENADES
939 // *************************
940 
941 #ifdef INCLUDE_INCENDIARY
FireThink(edict_t * ent)942 void FireThink (edict_t *ent)
943 {
944 	if(level.time > ent->wait)
945 		G_FreeEdict(ent);
946 	else
947 	{
948 		ent->s.frame++;
949 		if(ent->s.frame>10)
950 			ent->s.frame = 0;
951 		ent->nextthink = level.time + 0.05;
952 		ent->think = FireThink;
953 	}
954 }
955 
956 #define FIRE_HEIGHT		64
957 #define FIRE_RADIUS		64
958 #define FIRE_DAMAGE		3
959 #define FIRE_DURATION	15
960 
StartFire(edict_t * fireOwner,vec3_t fireOrigin,float fireDuration,float fireDamage)961 edict_t *StartFire(edict_t *fireOwner, vec3_t fireOrigin, float fireDuration, float fireDamage)
962 {
963 	edict_t	*fire;
964 
965 	fire = G_Spawn();
966 	VectorCopy (fireOrigin, fire->s.origin);
967 	fire->movetype = MOVETYPE_TOSS;
968 	fire->solid = SOLID_TRIGGER;
969 	VectorSet(fire->mins, -FIRE_RADIUS, -FIRE_RADIUS, 0);
970 	VectorSet(fire->maxs, FIRE_RADIUS, FIRE_RADIUS, FIRE_HEIGHT);
971 
972 	fire->s.sound = gi.soundindex ("weapons/incend.wav");
973 	fire->s.modelindex = gi.modelindex ("models/objects/fire/tris.md2");
974 
975 	fire->owner = fireOwner;
976 	fire->touch = hurt_touch;
977 	fire->nextthink = level.time + 0.05;
978 	fire->wait = level.time + fireDuration;
979 	fire->think = FireThink;
980 //	fire->nextthink = level.time + fireDuration;
981 //	fire->think = G_FreeEdict;
982 	fire->dmg = fireDamage;
983 	fire->classname = "incendiary_fire";
984 
985 	gi.linkentity (fire);
986 
987 //	gi.sound (fire, CHAN_VOICE, gi.soundindex ("weapons/incend.wav"), 1, ATTN_NORM, 0);
988 	return fire;
989 }
990 
Incendiary_Explode(edict_t * ent)991 static void Incendiary_Explode (edict_t *ent)
992 {
993 	vec3_t		origin;
994 
995 	if (ent->owner->client)
996 		PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
997 
998 	//FIXME: if we are onground then raise our Z just a bit since we are a point?
999 	T_RadiusDamage(ent, ent->owner, ent->dmg, NULL, ent->dmg_radius, DAMAGE_FIRE);
1000 
1001 	VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
1002 	gi.WriteByte (svc_temp_entity);
1003 	if (ent->groundentity)
1004 		gi.WriteByte (TE_GRENADE_EXPLOSION);
1005 	else
1006 		gi.WriteByte (TE_ROCKET_EXPLOSION);
1007 	gi.WritePosition (origin);
1008 	gi.multicast (ent->s.origin, MULTICAST_PVS);
1009 
1010 	StartFire(ent->owner, ent->s.origin, FIRE_DURATION, FIRE_DAMAGE);
1011 
1012 	G_FreeEdict (ent);
1013 
1014 }
1015 
Incendiary_Touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1016 static void Incendiary_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1017 {
1018 	if (other == ent->owner)
1019 		return;
1020 
1021 	if (surf && (surf->flags & SURF_SKY))
1022 	{
1023 		G_FreeEdict (ent);
1024 		return;
1025 	}
1026 
1027 	if (!(other->svflags & SVF_MONSTER) && !(ent->client))
1028 //	if (!other->takedamage)
1029 	{
1030 		if (ent->spawnflags & 1)
1031 		{
1032 			if (random() > 0.5)
1033 				gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
1034 			else
1035 				gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
1036 		}
1037 		else
1038 		{
1039 			if (random() > 0.5)
1040 				gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
1041 			else
1042 				gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb2b.wav"), 1, ATTN_NORM, 0);
1043 		}
1044 		return;
1045 	}
1046 
1047 	Incendiary_Explode (ent);
1048 }
1049 
fire_incendiary_grenade(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int speed,float timer,float damage_radius)1050 void fire_incendiary_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
1051 {
1052 	edict_t	*grenade;
1053 	vec3_t	dir;
1054 	vec3_t	forward, right, up;
1055 
1056 	vectoangles2 (aimdir, dir);
1057 	AngleVectors (dir, forward, right, up);
1058 
1059 	grenade = G_Spawn();
1060 	VectorCopy (start, grenade->s.origin);
1061 	VectorScale (aimdir, speed, grenade->velocity);
1062 	VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
1063 	VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
1064 	VectorSet (grenade->avelocity, 300, 300, 300);
1065 	grenade->movetype = MOVETYPE_BOUNCE;
1066 	grenade->clipmask = MASK_SHOT;
1067 	grenade->solid = SOLID_BBOX;
1068 	grenade->s.effects |= EF_GRENADE;
1069 //	if (self->client)
1070 //		grenade->s.effects &= ~EF_TELEPORT;
1071 	VectorClear (grenade->mins);
1072 	VectorClear (grenade->maxs);
1073 	grenade->s.modelindex = gi.modelindex ("models/projectiles/incend/tris.md2");
1074 	grenade->owner = self;
1075 	grenade->touch = Incendiary_Touch;
1076 	grenade->nextthink = level.time + timer;
1077 	grenade->think = Incendiary_Explode;
1078 	grenade->dmg = damage;
1079 	grenade->dmg_radius = damage_radius;
1080 	grenade->classname = "incendiary_grenade";
1081 
1082 	gi.linkentity (grenade);
1083 }
1084 #endif
1085 
1086 // *************************
1087 // MELEE WEAPONS
1088 // *************************
1089 
1090 #ifdef INCLUDE_MELEE
fire_player_melee(edict_t * self,vec3_t start,vec3_t aim,int reach,int damage,int kick,int quiet,int mod)1091 void fire_player_melee (edict_t *self, vec3_t start, vec3_t aim, int reach, int damage, int kick, int quiet, int mod)
1092 {
1093 	vec3_t		forward, right, up;
1094 	vec3_t		v;
1095 	vec3_t		point;
1096 	trace_t		tr;
1097 
1098 	vectoangles2 (aim, v);
1099 	AngleVectors (v, forward, right, up);
1100 	VectorNormalize (forward);
1101 	VectorMA( start, reach, forward, point);
1102 
1103 	//see if the hit connects
1104 	tr = gi.trace(start, NULL, NULL, point, self, MASK_SHOT);
1105 	if(tr.fraction ==  1.0)
1106 	{
1107 		if(!quiet)
1108 			gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/swish.wav"), 1, ATTN_NORM, 0);
1109 		//FIXME some sound here?
1110 		return;
1111 	}
1112 
1113 	if(tr.ent->takedamage == DAMAGE_YES || tr.ent->takedamage == DAMAGE_AIM)
1114 	{
1115 		// pull the player forward if you do damage
1116 		VectorMA(self->velocity, 75, forward, self->velocity);
1117 		VectorMA(self->velocity, 75, up, self->velocity);
1118 
1119 		// do the damage
1120 		// FIXME - make the damage appear at right spot and direction
1121 		if(mod == MOD_CHAINFIST)
1122 			T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2,
1123 						DAMAGE_DESTROY_ARMOR | DAMAGE_NO_KNOCKBACK, mod);
1124 		else
1125 			T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, mod);
1126 
1127 		if(!quiet)
1128 			gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/meatht.wav"), 1, ATTN_NORM, 0);
1129 	}
1130 	else
1131 	{
1132 		if(!quiet)
1133 			gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink1.wav"), 1, ATTN_NORM, 0);
1134 
1135 		VectorScale (tr.plane.normal, 256, point);
1136 		gi.WriteByte (svc_temp_entity);
1137 		gi.WriteByte (TE_GUNSHOT);
1138 		gi.WritePosition (tr.endpos);
1139 		gi.WriteDir (point);
1140 		gi.multicast (tr.endpos, MULTICAST_PVS);
1141 	}
1142 }
1143 #endif
1144 
1145 // *************************
1146 // NUKE
1147 // *************************
1148 
1149 #ifdef INCLUDE_NUKE
1150 #define	NUKE_DELAY			4
1151 #define NUKE_TIME_TO_LIVE	6
1152 //#define NUKE_TIME_TO_LIVE	40
1153 #define NUKE_RADIUS			512
1154 #define NUKE_DAMAGE			400
1155 #define	NUKE_QUAKE_TIME		3
1156 #define NUKE_QUAKE_STRENGTH	100
1157 
Nuke_Quake(edict_t * self)1158 void Nuke_Quake (edict_t *self)
1159 {
1160 	int		i;
1161 	edict_t	*e;
1162 
1163 	if (self->last_move_time < level.time)
1164 	{
1165 		gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 0.75, ATTN_NONE, 0);
1166 		self->last_move_time = level.time + 0.5;
1167 	}
1168 
1169 	for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
1170 	{
1171 		if (!e->inuse)
1172 			continue;
1173 		if (!e->client)
1174 			continue;
1175 		if (!e->groundentity)
1176 			continue;
1177 
1178 		e->groundentity = NULL;
1179 		e->velocity[0] += crandom()* 150;
1180 		e->velocity[1] += crandom()* 150;
1181 		e->velocity[2] = self->speed * (100.0 / e->mass);
1182 	}
1183 
1184 	if (level.time < self->timestamp)
1185 		self->nextthink = level.time + FRAMETIME;
1186 	else
1187 		G_FreeEdict (self);
1188 }
1189 
1190 
Nuke_Explode(edict_t * ent)1191 static void Nuke_Explode (edict_t *ent)
1192 {
1193 //	vec3_t		origin;
1194 
1195 //	nuke_framenum = level.framenum + 20;
1196 
1197 	if (ent->teammaster->client)
1198 		PlayerNoise(ent->teammaster, ent->s.origin, PNOISE_IMPACT);
1199 
1200 	T_RadiusNukeDamage(ent, ent->teammaster, ent->dmg, ent, ent->dmg_radius, MOD_NUKE);
1201 
1202 //	VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
1203 	if (ent->dmg > NUKE_DAMAGE)
1204 		gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
1205 
1206 	gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/grenlx1a.wav"), 1, ATTN_NONE, 0);
1207 /*
1208 	gi.WriteByte (svc_temp_entity);
1209 	if (ent->groundentity)
1210 		gi.WriteByte (TE_GRENADE_EXPLOSION);
1211 	else
1212 		gi.WriteByte (TE_ROCKET_EXPLOSION);
1213 	gi.WritePosition (origin);
1214 	gi.multicast (ent->s.origin, MULTICAST_PVS);
1215 */
1216 
1217 	//	BecomeExplosion1(ent);
1218 
1219 	gi.WriteByte (svc_temp_entity);
1220 	gi.WriteByte (TE_EXPLOSION1_BIG);
1221 	gi.WritePosition (ent->s.origin);
1222 	gi.multicast (ent->s.origin, MULTICAST_PVS);
1223 
1224 	gi.WriteByte (svc_temp_entity);
1225 	gi.WriteByte (TE_NUKEBLAST);
1226 	gi.WritePosition (ent->s.origin);
1227 	gi.multicast (ent->s.origin, MULTICAST_ALL);
1228 
1229 	// become a quake
1230 	ent->svflags |= SVF_NOCLIENT;
1231 	ent->noise_index = gi.soundindex ("world/rumble.wav");
1232 	ent->think = Nuke_Quake;
1233 	ent->speed = NUKE_QUAKE_STRENGTH;
1234 	ent->timestamp = level.time + NUKE_QUAKE_TIME;
1235 	ent->nextthink = level.time + FRAMETIME;
1236 	ent->last_move_time = 0;
1237 }
1238 
nuke_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1239 void nuke_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1240 {
1241 	self->takedamage = DAMAGE_NO;
1242 	if ((attacker) && !(strcmp(attacker->classname, "nuke")))
1243 	{
1244 //		if ((g_showlogic) && (g_showlogic->value))
1245 //			gi.dprintf ("nuke nuked by a nuke, not nuking\n");
1246 		G_FreeEdict (self);
1247 		return;
1248 	}
1249 	Nuke_Explode(self);
1250 }
1251 
Nuke_Think(edict_t * ent)1252 void Nuke_Think(edict_t *ent)
1253 {
1254 	float attenuation, default_atten = 1.8;
1255 	int		damage_multiplier, muzzleflash;
1256 
1257 //	gi.dprintf ("player range: %2.2f    damage radius: %2.2f\n", realrange (ent, ent->teammaster), ent->dmg_radius*2);
1258 
1259 	damage_multiplier = ent->dmg/NUKE_DAMAGE;
1260 	switch (damage_multiplier)
1261 	{
1262 	case 1:
1263 		attenuation = default_atten/1.4;
1264 		muzzleflash = MZ_NUKE1;
1265 		break;
1266 	case 2:
1267 		attenuation = default_atten/2.0;
1268 		muzzleflash = MZ_NUKE2;
1269 		break;
1270 	case 4:
1271 		attenuation = default_atten/3.0;
1272 		muzzleflash = MZ_NUKE4;
1273 		break;
1274 	case 8:
1275 		attenuation = default_atten/5.0;
1276 		muzzleflash = MZ_NUKE8;
1277 		break;
1278 	default:
1279 //		if ((g_showlogic) && (g_showlogic->value))
1280 //			gi.dprintf ("default attenuation used for nuke!\n");
1281 		attenuation = default_atten;
1282 		muzzleflash = MZ_NUKE1;
1283 		break;
1284 	}
1285 
1286 	if(ent->wait < level.time)
1287 		Nuke_Explode(ent);
1288 	else if (level.time >= (ent->wait - NUKE_TIME_TO_LIVE))
1289 	{
1290 		ent->s.frame++;
1291 //		if ((g_showlogic) && (g_showlogic->value))
1292 //			gi.dprintf ("nuke frame %d\n", ent->s.frame);
1293 		if(ent->s.frame > 11)
1294 			ent->s.frame = 6;
1295 
1296 		if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA))
1297 		{
1298 			Nuke_Explode (ent);
1299 			return;
1300 		}
1301 
1302 		ent->think = Nuke_Think;
1303 		ent->nextthink = level.time + 0.1;
1304 		ent->health = 1;
1305 		ent->owner = NULL;
1306 
1307 		gi.WriteByte (svc_muzzleflash);
1308 		gi.WriteShort (ent-g_edicts);
1309 		gi.WriteByte (muzzleflash);
1310 		gi.multicast (ent->s.origin, MULTICAST_PVS);
1311 
1312 		if (ent->timestamp <= level.time)
1313 		{
1314 /*			gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn.wav"), 1, ATTN_NORM, 0);
1315 			ent->timestamp += 10.0;
1316 		}
1317 */
1318 
1319 			if ((ent->wait - level.time) <= (NUKE_TIME_TO_LIVE/2.0))
1320 			{
1321 //				ent->s.sound = gi.soundindex ("weapons/nukewarn.wav");
1322 //				gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
1323 				gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
1324 //				gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
1325 //				gi.dprintf ("time %2.2f\n", ent->wait-level.time);
1326 				ent->timestamp = level.time + 0.3;
1327 			}
1328 			else
1329 			{
1330 				gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
1331 //				gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
1332 				ent->timestamp = level.time + 0.5;
1333 //				gi.dprintf ("time %2.2f\n", ent->wait-level.time);
1334 			}
1335 		}
1336 	}
1337 	else
1338 	{
1339 		if (ent->timestamp <= level.time)
1340 		{
1341 			gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
1342 //			gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
1343 //				gi.dprintf ("time %2.2f\n", ent->wait-level.time);
1344 			ent->timestamp = level.time + 1.0;
1345 		}
1346 		ent->nextthink = level.time + FRAMETIME;
1347 	}
1348 }
1349 
nuke_bounce(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1350 void nuke_bounce (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1351 {
1352 	if (random() > 0.5)
1353 		gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
1354 	else
1355 		gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
1356 }
1357 
1358 
1359 extern byte P_DamageModifier(edict_t *ent);
1360 
fire_nuke(edict_t * self,vec3_t start,vec3_t aimdir,int speed)1361 void fire_nuke (edict_t *self, vec3_t start, vec3_t aimdir, int speed)
1362 {
1363 	edict_t	*nuke;
1364 	vec3_t	dir;
1365 	vec3_t	forward, right, up;
1366 	int		damage_modifier;
1367 
1368 	damage_modifier = (int) P_DamageModifier (self);
1369 
1370 	vectoangles2 (aimdir, dir);
1371 	AngleVectors (dir, forward, right, up);
1372 
1373 	nuke = G_Spawn();
1374 	VectorCopy (start, nuke->s.origin);
1375 	VectorScale (aimdir, speed, nuke->velocity);
1376 
1377 	VectorMA (nuke->velocity, 200 + crandom() * 10.0, up, nuke->velocity);
1378 	VectorMA (nuke->velocity, crandom() * 10.0, right, nuke->velocity);
1379 	VectorClear (nuke->avelocity);
1380 	VectorClear (nuke->s.angles);
1381 	nuke->movetype = MOVETYPE_BOUNCE;
1382 	nuke->clipmask = MASK_SHOT;
1383 	nuke->solid = SOLID_BBOX;
1384 	nuke->s.effects |= EF_GRENADE;
1385 	nuke->s.renderfx |= RF_IR_VISIBLE;
1386 	VectorSet (nuke->mins, -8, -8, 0);
1387 	VectorSet (nuke->maxs, 8, 8, 16);
1388 	nuke->s.modelindex = gi.modelindex ("models/weapons/g_nuke/tris.md2");
1389 	nuke->owner = self;
1390 	nuke->teammaster = self;
1391 	nuke->nextthink = level.time + FRAMETIME;
1392 	nuke->wait = level.time + NUKE_DELAY + NUKE_TIME_TO_LIVE;
1393 	nuke->think = Nuke_Think;
1394 	nuke->touch = nuke_bounce;
1395 
1396 	nuke->health = 10000;
1397 	nuke->takedamage = DAMAGE_YES;
1398 	nuke->svflags |= SVF_DAMAGEABLE;
1399 	nuke->dmg = NUKE_DAMAGE * damage_modifier;
1400 	if (damage_modifier == 1)
1401 		nuke->dmg_radius = NUKE_RADIUS;
1402 	else
1403 		nuke->dmg_radius = NUKE_RADIUS + NUKE_RADIUS*(0.25*(float)damage_modifier);
1404 	// this yields 1.0, 1.5, 2.0, 3.0 times radius
1405 
1406 //	if ((g_showlogic) && (g_showlogic->value))
1407 //		gi.dprintf ("nuke modifier = %d, damage = %d, radius = %f\n", damage_modifier, nuke->dmg, nuke->dmg_radius);
1408 
1409 	nuke->classname = "nuke";
1410 	nuke->die = nuke_die;
1411 
1412 	gi.linkentity (nuke);
1413 }
1414 #endif
1415 
1416 // *************************
1417 // TESLA
1418 // *************************
1419 
1420 #ifdef INCLUDE_TESLA
1421 #define TESLA_TIME_TO_LIVE		30
1422 #define TESLA_DAMAGE_RADIUS		128
1423 #define TESLA_DAMAGE			3		// 3
1424 #define TESLA_KNOCKBACK			8
1425 
1426 #define	TESLA_ACTIVATE_TIME		3
1427 
1428 #define TESLA_EXPLOSION_DAMAGE_MULT		50		// this is the amount the damage is multiplied by for underwater explosions
1429 #define	TESLA_EXPLOSION_RADIUS			200
1430 
tesla_remove(edict_t * self)1431 void tesla_remove (edict_t *self)
1432 {
1433 	edict_t		*cur, *next;
1434 
1435 	self->takedamage = DAMAGE_NO;
1436 	if(self->teamchain)
1437 	{
1438 		cur = self->teamchain;
1439 		while(cur)
1440 		{
1441 			next = cur->teamchain;
1442 			G_FreeEdict ( cur );
1443 			cur = next;
1444 		}
1445 	}
1446 	else if (self->air_finished)
1447 		gi.dprintf ("tesla without a field!\n");
1448 
1449 	self->owner = self->teammaster;	// Going away, set the owner correctly.
1450 	// PGM - grenade explode does damage to self->enemy
1451 	self->enemy = NULL;
1452 
1453 	// play quad sound if quadded and an underwater explosion
1454 	if ((self->dmg_radius) && (self->dmg > (TESLA_DAMAGE*TESLA_EXPLOSION_DAMAGE_MULT)))
1455 		gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
1456 
1457 	Grenade_Explode(self);
1458 }
1459 
tesla_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1460 void tesla_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1461 {
1462 //	gi.dprintf("tesla killed\n");
1463 	tesla_remove(self);
1464 }
1465 
tesla_blow(edict_t * self)1466 void tesla_blow (edict_t *self)
1467 {
1468 //	T_RadiusDamage(self, self->owner, TESLA_EXPLOSION_DAMAGE, NULL, TESLA_EXPLOSION_RADIUS, MOD_TESLA);
1469 	self->dmg = self->dmg * TESLA_EXPLOSION_DAMAGE_MULT;
1470 	self->dmg_radius = TESLA_EXPLOSION_RADIUS;
1471 	tesla_remove(self);
1472 }
1473 
1474 
tesla_zap(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1475 void tesla_zap (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1476 {
1477 }
1478 
tesla_think_active(edict_t * self)1479 void tesla_think_active (edict_t *self)
1480 {
1481 	int		i,num;
1482 	edict_t	*touch[MAX_EDICTS], *hit;
1483 	vec3_t	dir, start;
1484 	trace_t	tr;
1485 
1486 	if(level.time > self->air_finished)
1487 	{
1488 		tesla_remove(self);
1489 		return;
1490 	}
1491 
1492 	VectorCopy(self->s.origin, start);
1493 	start[2] += 16;
1494 
1495 	num = gi.BoxEdicts(self->teamchain->absmin, self->teamchain->absmax, touch, MAX_EDICTS, AREA_SOLID);
1496 	for(i=0;i<num;i++)
1497 	{
1498 		// if the tesla died while zapping things, stop zapping.
1499 		if(!(self->inuse))
1500 			break;
1501 
1502 		hit=touch[i];
1503 		if(!hit->inuse)
1504 			continue;
1505 		if(hit == self)
1506 			continue;
1507 		if(hit->health < 1)
1508 			continue;
1509 		// don't hit clients in single-player or coop
1510 		if(hit->client)
1511 			if (coop->value || !deathmatch->value)
1512 				continue;
1513 		if(!(hit->svflags & (SVF_MONSTER | SVF_DAMAGEABLE)) && !hit->client)
1514 			continue;
1515 
1516 		tr = gi.trace(start, vec3_origin, vec3_origin, hit->s.origin, self, MASK_SHOT);
1517 		if(tr.fraction==1 || tr.ent==hit)// || tr.ent->client || (tr.ent->svflags & (SVF_MONSTER | SVF_DAMAGEABLE)))
1518 		{
1519 			VectorSubtract(hit->s.origin, start, dir);
1520 
1521 			// PMM - play quad sound if it's above the "normal" damage
1522 			if (self->dmg > TESLA_DAMAGE)
1523 				gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
1524 
1525 			// PGM - don't do knockback to walking monsters
1526 			if((hit->svflags & SVF_MONSTER) && !(hit->flags & (FL_FLY|FL_SWIM)))
1527 				T_Damage (hit, self, self->teammaster, dir, tr.endpos, tr.plane.normal,
1528 					self->dmg, 0, 0, MOD_TESLA);
1529 			else
1530 				T_Damage (hit, self, self->teammaster, dir, tr.endpos, tr.plane.normal,
1531 					self->dmg, TESLA_KNOCKBACK, 0, MOD_TESLA);
1532 
1533 			gi.WriteByte (svc_temp_entity);
1534 			gi.WriteByte (TE_LIGHTNING);
1535 			gi.WriteShort (hit - g_edicts);			// destination entity
1536 			gi.WriteShort (self - g_edicts);		// source entity
1537 			gi.WritePosition (tr.endpos);
1538 			gi.WritePosition (start);
1539 			gi.multicast (start, MULTICAST_PVS);
1540 		}
1541 	}
1542 
1543 	if(self->inuse)
1544 	{
1545 		self->think = tesla_think_active;
1546 		self->nextthink = level.time + FRAMETIME;
1547 	}
1548 }
1549 
tesla_activate(edict_t * self)1550 void tesla_activate (edict_t *self)
1551 {
1552 	edict_t		*trigger;
1553 	edict_t		*search;
1554 
1555 	if (gi.pointcontents (self->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WATER))
1556 	{
1557 		tesla_blow (self);
1558 		return;
1559 	}
1560 
1561 	// only check for spawn points in deathmatch
1562 	if (deathmatch->value)
1563 	{
1564 		search = NULL;
1565 		while ((search = findradius(search, self->s.origin, 1.5*TESLA_DAMAGE_RADIUS)) != NULL)
1566 		{
1567 			//if (!search->takedamage)
1568 			//	continue;
1569 			// if it's a monster or player with health > 0
1570 			// or it's a deathmatch start point
1571 			// and we can see it
1572 			// blow up
1573 			if(search->classname)
1574 			{
1575 				if (   ( (!strcmp(search->classname, "info_player_deathmatch"))
1576 					|| (!strcmp(search->classname, "info_player_start"))
1577 					|| (!strcmp(search->classname, "info_player_coop"))
1578 					|| (!strcmp(search->classname, "misc_teleporter_dest"))
1579 					)
1580 					&& (visible (search, self))
1581 				   )
1582 				{
1583 //					if ((g_showlogic) && (g_showlogic->value))
1584 //						gi.dprintf ("Tesla to close to %s, removing!\n", search->classname);
1585 					tesla_remove (self);
1586 					return;
1587 				}
1588 			}
1589 		}
1590 	}
1591 
1592 	trigger = G_Spawn();
1593 //	if (trigger->nextthink)
1594 //	{
1595 //		if ((g_showlogic) && (g_showlogic->value))
1596 //			gi.dprintf ("tesla_activate:  fixing nextthink\n");
1597 //		trigger->nextthink = 0;
1598 //	}
1599 	VectorCopy (self->s.origin, trigger->s.origin);
1600 	VectorSet (trigger->mins, -TESLA_DAMAGE_RADIUS, -TESLA_DAMAGE_RADIUS, self->mins[2]);
1601 	VectorSet (trigger->maxs, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS);
1602 	trigger->movetype = MOVETYPE_NONE;
1603 	trigger->solid = SOLID_TRIGGER;
1604 	trigger->owner = self;
1605 	trigger->touch = tesla_zap;
1606 	trigger->classname = "tesla trigger";
1607 	// doesn't need to be marked as a teamslave since the move code for bounce looks for teamchains
1608 	gi.linkentity (trigger);
1609 
1610 	VectorClear (self->s.angles);
1611 	// clear the owner if in deathmatch
1612 	if (deathmatch->value)
1613 		self->owner = NULL;
1614 	self->teamchain = trigger;
1615 	self->think = tesla_think_active;
1616 	self->nextthink = level.time + FRAMETIME;
1617 	self->air_finished = level.time + TESLA_TIME_TO_LIVE;
1618 }
1619 
tesla_think(edict_t * ent)1620 void tesla_think (edict_t *ent)
1621 {
1622 	if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA))
1623 	{
1624 		tesla_remove (ent);
1625 		return;
1626 	}
1627 	VectorClear (ent->s.angles);
1628 
1629 	if(!(ent->s.frame))
1630 		gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/teslaopen.wav"), 1, ATTN_NORM, 0);
1631 
1632 	ent->s.frame++;
1633 	if(ent->s.frame > 14)
1634 	{
1635 		ent->s.frame = 14;
1636 		ent->think = tesla_activate;
1637 		ent->nextthink = level.time + 0.1;
1638 	}
1639 	else
1640 	{
1641 		if(ent->s.frame > 9)
1642 		{
1643 			if(ent->s.frame == 10)
1644 			{
1645 				if (ent->owner && ent->owner->client)
1646 				{
1647 					PlayerNoise(ent->owner, ent->s.origin, PNOISE_WEAPON);		// PGM
1648 				}
1649 				ent->s.skinnum = 1;
1650 			}
1651 			else if(ent->s.frame == 12)
1652 				ent->s.skinnum = 2;
1653 			else if(ent->s.frame == 14)
1654 				ent->s.skinnum = 3;
1655 		}
1656 		ent->think = tesla_think;
1657 		ent->nextthink = level.time + 0.1;
1658 	}
1659 }
1660 
tesla_lava(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1661 void tesla_lava (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1662 {
1663 	vec3_t	land_point;
1664 
1665 	if (plane->normal)
1666 	{
1667 		VectorMA (ent->s.origin, -20.0, plane->normal, land_point);
1668 		if (gi.pointcontents (land_point) & (CONTENTS_SLIME|CONTENTS_LAVA))
1669 		{
1670 			tesla_blow (ent);
1671 			return;
1672 		}
1673 	}
1674 	if (random() > 0.5)
1675 		gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
1676 	else
1677 		gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
1678 }
1679 
fire_tesla(edict_t * self,vec3_t start,vec3_t aimdir,int damage_multiplier,int speed)1680 void fire_tesla (edict_t *self, vec3_t start, vec3_t aimdir, int damage_multiplier, int speed)
1681 {
1682 	edict_t	*tesla;
1683 	vec3_t	dir;
1684 	vec3_t	forward, right, up;
1685 
1686 	vectoangles2 (aimdir, dir);
1687 	AngleVectors (dir, forward, right, up);
1688 
1689 	tesla = G_Spawn();
1690 	VectorCopy (start, tesla->s.origin);
1691 	VectorScale (aimdir, speed, tesla->velocity);
1692 	VectorMA (tesla->velocity, 200 + crandom() * 10.0, up, tesla->velocity);
1693 	VectorMA (tesla->velocity, crandom() * 10.0, right, tesla->velocity);
1694 //	VectorCopy (dir, tesla->s.angles);
1695 	VectorClear (tesla->s.angles);
1696 	tesla->movetype = MOVETYPE_BOUNCE;
1697 	tesla->solid = SOLID_BBOX;
1698 	tesla->s.effects |= EF_GRENADE;
1699 	tesla->s.renderfx |= RF_IR_VISIBLE;
1700 //	VectorClear (tesla->mins);
1701 //	VectorClear (tesla->maxs);
1702 	VectorSet (tesla->mins, -12, -12, 0);
1703 	VectorSet (tesla->maxs, 12, 12, 20);
1704 	tesla->s.modelindex = gi.modelindex ("models/weapons/g_tesla/tris.md2");
1705 
1706 	tesla->owner = self;		// PGM - we don't want it owned by self YET.
1707 	tesla->teammaster = self;
1708 
1709 	tesla->wait = level.time + TESLA_TIME_TO_LIVE;
1710 	tesla->think = tesla_think;
1711 	tesla->nextthink = level.time + TESLA_ACTIVATE_TIME;
1712 
1713 	// blow up on contact with lava & slime code
1714 	tesla->touch = tesla_lava;
1715 
1716 	if(deathmatch->value)
1717 		// PMM - lowered from 50 - 7/29/1998
1718 		tesla->health = 20;
1719 	else
1720 		tesla->health = 30;		// FIXME - change depending on skill?
1721 
1722 	tesla->takedamage = DAMAGE_YES;
1723 	tesla->die = tesla_die;
1724 	tesla->dmg = TESLA_DAMAGE*damage_multiplier;
1725 //	tesla->dmg = 0;
1726 	tesla->classname = "tesla";
1727 	tesla->svflags |= SVF_DAMAGEABLE;
1728 	tesla->clipmask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
1729 	tesla->flags |= FL_MECHANICAL;
1730 
1731 	gi.linkentity (tesla);
1732 }
1733 #endif
1734 
1735 // *************************
1736 //  HEATBEAM
1737 // *************************
1738 
1739 #ifdef INCLUDE_BEAMS
fire_beams(edict_t * self,vec3_t start,vec3_t aimdir,vec3_t offset,int damage,int kick,int te_beam,int te_impact,int mod)1740 static void fire_beams (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t offset, int damage, int kick, int te_beam, int te_impact, int mod)
1741 {
1742 	trace_t		tr;
1743 	vec3_t		dir;
1744 	vec3_t		forward, right, up;
1745 	vec3_t		end;
1746 	vec3_t		water_start, endpoint;
1747 	qboolean	water = false, underwater = false;
1748 	int			content_mask = MASK_SHOT | MASK_WATER;
1749 	vec3_t		beam_endpt;
1750 
1751 //	tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
1752 //	if (!(tr.fraction < 1.0))
1753 //	{
1754 	vectoangles2 (aimdir, dir);
1755 	AngleVectors (dir, forward, right, up);
1756 
1757 	VectorMA (start, 8192, forward, end);
1758 
1759 	if (gi.pointcontents (start) & MASK_WATER)
1760 	{
1761 //		gi.dprintf ("Heat beam under water\n");
1762 		underwater = true;
1763 		VectorCopy (start, water_start);
1764 		content_mask &= ~MASK_WATER;
1765 	}
1766 
1767 	tr = gi.trace (start, NULL, NULL, end, self, content_mask);
1768 
1769 	// see if we hit water
1770 	if (tr.contents & MASK_WATER)
1771 	{
1772 		water = true;
1773 		VectorCopy (tr.endpos, water_start);
1774 
1775 		if (!VectorCompare (start, tr.endpos))
1776 		{
1777 			gi.WriteByte (svc_temp_entity);
1778 			gi.WriteByte (TE_HEATBEAM_SPARKS);
1779 //			gi.WriteByte (50);
1780 			gi.WritePosition (water_start);
1781 			gi.WriteDir (tr.plane.normal);
1782 //			gi.WriteByte (8);
1783 //			gi.WriteShort (60);
1784 			gi.multicast (tr.endpos, MULTICAST_PVS);
1785 		}
1786 		// re-trace ignoring water this time
1787 		tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT);
1788 	}
1789 	VectorCopy (tr.endpos, endpoint);
1790 //	}
1791 
1792 	// halve the damage if target underwater
1793 	if (water)
1794 	{
1795 		damage = damage /2;
1796 	}
1797 
1798 	// send gun puff / flash
1799 	if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
1800 	{
1801 		if (tr.fraction < 1.0)
1802 		{
1803 			if (tr.ent->takedamage)
1804 			{
1805 				T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_ENERGY, mod);
1806 			}
1807 			else
1808 			{
1809 				if ((!water) && (strncmp (tr.surface->name, "sky", 3)))
1810 				{
1811 					// This is the truncated steam entry - uses 1+1+2 extra bytes of data
1812 					gi.WriteByte (svc_temp_entity);
1813 					gi.WriteByte (TE_HEATBEAM_STEAM);
1814 //					gi.WriteByte (20);
1815 					gi.WritePosition (tr.endpos);
1816 					gi.WriteDir (tr.plane.normal);
1817 //					gi.WriteByte (0xe0);
1818 //					gi.WriteShort (60);
1819 					gi.multicast (tr.endpos, MULTICAST_PVS);
1820 
1821 					if (self->client)
1822 						PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
1823 				}
1824 			}
1825 		}
1826 	}
1827 
1828 	// if went through water, determine where the end and make a bubble trail
1829 	if ((water) || (underwater))
1830 	{
1831 		vec3_t	pos;
1832 
1833 		VectorSubtract (tr.endpos, water_start, dir);
1834 		VectorNormalize (dir);
1835 		VectorMA (tr.endpos, -2, dir, pos);
1836 		if (gi.pointcontents (pos) & MASK_WATER)
1837 			VectorCopy (pos, tr.endpos);
1838 		else
1839 			tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
1840 
1841 		VectorAdd (water_start, tr.endpos, pos);
1842 		VectorScale (pos, 0.5, pos);
1843 
1844 		gi.WriteByte (svc_temp_entity);
1845 		gi.WriteByte (TE_BUBBLETRAIL2);
1846 //		gi.WriteByte (8);
1847 		gi.WritePosition (water_start);
1848 		gi.WritePosition (tr.endpos);
1849 		gi.multicast (pos, MULTICAST_PVS);
1850 	}
1851 
1852 	if ((!underwater) && (!water))
1853 	{
1854 		VectorCopy (tr.endpos, beam_endpt);
1855 	}
1856 	else
1857 	{
1858 		VectorCopy (endpoint, beam_endpt);
1859 	}
1860 
1861 	gi.WriteByte (svc_temp_entity);
1862 	gi.WriteByte (te_beam);
1863 	gi.WriteShort (self - g_edicts);
1864 	gi.WritePosition (start);
1865 	gi.WritePosition (beam_endpt);
1866 	gi.multicast (self->s.origin, MULTICAST_ALL);
1867 
1868 }
1869 
1870 
1871 /*
1872 =================
1873 fire_heat
1874 
1875 Fires a single heat beam.  Zap.
1876 =================
1877 */
fire_heat(edict_t * self,vec3_t start,vec3_t aimdir,vec3_t offset,int damage,int kick,qboolean monster)1878 void fire_heat (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t offset, int damage, int kick, qboolean monster)
1879 {
1880 	if (monster)
1881 		fire_beams (self, start, aimdir, offset, damage, kick, TE_MONSTER_HEATBEAM, TE_HEATBEAM_SPARKS, MOD_HEATBEAM);
1882 	else
1883 		fire_beams (self, start, aimdir, offset, damage, kick, TE_HEATBEAM, TE_HEATBEAM_SPARKS, MOD_HEATBEAM);
1884 }
1885 
1886 #endif
1887 
1888 
1889 // *************************
1890 //	BLASTER 2
1891 // *************************
1892 
1893 /*
1894 =================
1895 fire_blaster2
1896 
1897 Fires a single green blaster bolt.  Used by monsters, generally.
1898 =================
1899 */
blaster2_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1900 void blaster2_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1901 {
1902 	int		mod;
1903 	int		damagestat;
1904 
1905 	if (other == self->owner)
1906 		return;
1907 
1908 	if (surf && (surf->flags & SURF_SKY))
1909 	{
1910 		G_FreeEdict (self);
1911 		return;
1912 	}
1913 
1914 	if (self->owner && self->owner->client)
1915 		PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
1916 
1917 	if (other->takedamage)
1918 	{
1919 		// the only time players will be firing blaster2 bolts will be from the
1920 		// defender sphere.
1921 		if(self->owner->client)
1922 			mod = MOD_DEFENDER_SPHERE;
1923 		else
1924 			mod = MOD_BLASTER2;
1925 
1926 		if (self->owner)
1927 		{
1928 			damagestat = self->owner->takedamage;
1929 			self->owner->takedamage = DAMAGE_NO;
1930 			if (self->dmg >= 5)
1931 				T_RadiusDamage(self, self->owner, self->dmg*3, other, self->dmg_radius, 0);
1932 			T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
1933 			self->owner->takedamage = damagestat;
1934 		}
1935 		else
1936 		{
1937 			if (self->dmg >= 5)
1938 				T_RadiusDamage(self, self->owner, self->dmg*3, other, self->dmg_radius, 0);
1939 			T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
1940 		}
1941 	}
1942 	else
1943 	{
1944 		//PMM - yeowch this will get expensive
1945 		if (self->dmg >= 5)
1946 			T_RadiusDamage(self, self->owner, self->dmg*3, self->owner, self->dmg_radius, 0);
1947 
1948 		gi.WriteByte (svc_temp_entity);
1949 		gi.WriteByte (TE_BLASTER2);
1950 		gi.WritePosition (self->s.origin);
1951 		if (!plane)
1952 			gi.WriteDir (vec3_origin);
1953 		else
1954 			gi.WriteDir (plane->normal);
1955 		gi.multicast (self->s.origin, MULTICAST_PVS);
1956 	}
1957 
1958 	G_FreeEdict (self);
1959 }
1960 
fire_blaster2(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,int effect,qboolean hyper)1961 void fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper)
1962 {
1963 	edict_t	*bolt;
1964 	trace_t	tr;
1965 
1966 	VectorNormalize (dir);
1967 
1968 	bolt = G_Spawn();
1969 	VectorCopy (start, bolt->s.origin);
1970 	VectorCopy (start, bolt->s.old_origin);
1971 	vectoangles2 (dir, bolt->s.angles);
1972 	VectorScale (dir, speed, bolt->velocity);
1973 	bolt->movetype = MOVETYPE_FLYMISSILE;
1974 	bolt->clipmask = MASK_SHOT;
1975 	bolt->solid = SOLID_BBOX;
1976 	bolt->s.effects |= effect;
1977 	VectorClear (bolt->mins);
1978 	VectorClear (bolt->maxs);
1979 
1980 		if (effect)
1981 			bolt->s.effects |= EF_TRACKER;
1982 		bolt->dmg_radius = 128;
1983 		bolt->s.modelindex = gi.modelindex ("models/proj/laser2/tris.md2");
1984 		bolt->touch = blaster2_touch;
1985 
1986 	bolt->owner = self;
1987 	bolt->nextthink = level.time + 2;
1988 	bolt->think = G_FreeEdict;
1989 	bolt->dmg = damage;
1990 	bolt->classname = "bolt";
1991  	gi.linkentity (bolt);
1992 
1993 	if (self->client)
1994 		check_dodge (self, bolt->s.origin, dir, speed);
1995 
1996 	tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
1997 	if (tr.fraction < 1.0)
1998 	{
1999 		VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
2000 		bolt->touch (bolt, tr.ent, NULL, NULL);
2001 	}
2002 }
2003 
2004 // *************************
2005 // tracker
2006 // *************************
2007 
2008 /*
2009 void tracker_boom_think (edict_t *self)
2010 {
2011 	self->s.frame--;
2012 	if(self->s.frame < 0)
2013 		G_FreeEdict(self);
2014 	else
2015 		self->nextthink = level.time + 0.1;
2016 }
2017 
2018 void tracker_boom_spawn (vec3_t origin)
2019 {
2020 	edict_t *boom;
2021 
2022 	boom = G_Spawn();
2023 	VectorCopy (origin, boom->s.origin);
2024 	boom->s.modelindex = gi.modelindex ("models/items/spawngro/tris.md2");
2025 	boom->s.skinnum = 1;
2026 	boom->s.frame = 2;
2027 	boom->classname = "tracker boom";
2028 	gi.linkentity (boom);
2029 
2030 	boom->think = tracker_boom_think;
2031 	boom->nextthink = level.time + 0.1;
2032 	//PMM
2033 //	boom->s.renderfx |= RF_TRANSLUCENT;
2034 	boom->s.effects |= EF_SPHERETRANS;
2035 	//pmm
2036 }
2037 */
2038 
2039 #define TRACKER_DAMAGE_FLAGS	(DAMAGE_NO_POWER_ARMOR | DAMAGE_ENERGY | DAMAGE_NO_KNOCKBACK)
2040 #define TRACKER_IMPACT_FLAGS	(DAMAGE_NO_POWER_ARMOR | DAMAGE_ENERGY)
2041 
2042 #define TRACKER_DAMAGE_TIME		0.5		// seconds
2043 
tracker_pain_daemon_think(edict_t * self)2044 void tracker_pain_daemon_think (edict_t *self)
2045 {
2046 	static vec3_t	pain_normal = { 0, 0, 1 };
2047 	int				hurt;
2048 
2049 	if(!self->inuse)
2050 		return;
2051 
2052 	if((level.time - self->timestamp) > TRACKER_DAMAGE_TIME)
2053 	{
2054 		if(!self->enemy->client)
2055 			self->enemy->s.effects &= ~EF_TRACKERTRAIL;
2056 		G_FreeEdict (self);
2057 	}
2058 	else
2059 	{
2060 		if(self->enemy->health > 0)
2061 		{
2062 //			gi.dprintf("ouch %x\n", self);
2063 			T_Damage (self->enemy, self, self->owner, vec3_origin, self->enemy->s.origin, pain_normal,
2064 						self->dmg, 0, TRACKER_DAMAGE_FLAGS, MOD_TRACKER);
2065 
2066 			// if we kill the player, we'll be removed.
2067 			if(self->inuse)
2068 			{
2069 				// if we killed a monster, gib them.
2070 				if (self->enemy->health < 1)
2071 				{
2072 					if(self->enemy->gib_health)
2073 						hurt = - self->enemy->gib_health;
2074 					else
2075 						hurt = 500;
2076 
2077 //					gi.dprintf("non-player killed. ensuring gib!  %d\n", hurt);
2078 					T_Damage (self->enemy, self, self->owner, vec3_origin, self->enemy->s.origin,
2079 								pain_normal, hurt, 0, TRACKER_DAMAGE_FLAGS, MOD_TRACKER);
2080 				}
2081 
2082 				if(self->enemy->client)
2083 					self->enemy->client->tracker_pain_framenum = level.framenum + 1;
2084 				else
2085 					self->enemy->s.effects |= EF_TRACKERTRAIL;
2086 
2087 				self->nextthink = level.time + FRAMETIME;
2088 			}
2089 		}
2090 		else
2091 		{
2092 			if(!self->enemy->client)
2093 				self->enemy->s.effects &= ~EF_TRACKERTRAIL;
2094 			G_FreeEdict (self);
2095 		}
2096 	}
2097 }
2098 
tracker_pain_daemon_spawn(edict_t * owner,edict_t * enemy,int damage)2099 void tracker_pain_daemon_spawn (edict_t *owner, edict_t *enemy, int damage)
2100 {
2101 	edict_t	 *daemon;
2102 
2103 	if(enemy == NULL)
2104 		return;
2105 
2106 	daemon = G_Spawn();
2107 	daemon->classname = "pain daemon";
2108 	daemon->think = tracker_pain_daemon_think;
2109 	daemon->nextthink = level.time + FRAMETIME;
2110 	daemon->timestamp = level.time;
2111 	daemon->owner = owner;
2112 	daemon->enemy = enemy;
2113 	daemon->dmg = damage;
2114 }
2115 
tracker_explode(edict_t * self,cplane_t * plane)2116 void tracker_explode (edict_t *self, cplane_t *plane)
2117 {
2118 	vec3_t	dir;
2119 
2120 	if(!plane)
2121 		VectorClear (dir);
2122 	else
2123 		VectorScale (plane->normal, 256, dir);
2124 
2125 	gi.WriteByte (svc_temp_entity);
2126 	gi.WriteByte (TE_TRACKER_EXPLOSION);
2127 	gi.WritePosition (self->s.origin);
2128 	gi.multicast (self->s.origin, MULTICAST_PVS);
2129 
2130 //	gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/disrupthit.wav"), 1, ATTN_NORM, 0);
2131 //	tracker_boom_spawn(self->s.origin);
2132 
2133 	G_FreeEdict (self);
2134 }
2135 
tracker_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)2136 void tracker_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
2137 {
2138 	float	damagetime;
2139 
2140 	if (other == self->owner)
2141 		return;
2142 
2143 	if (surf && (surf->flags & SURF_SKY))
2144 	{
2145 		G_FreeEdict (self);
2146 		return;
2147 	}
2148 
2149 	if (self->client)
2150 		PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
2151 
2152 	if (other->takedamage)
2153 	{
2154 		if((other->svflags & SVF_MONSTER) || other->client)
2155 		{
2156 			if(other->health > 0)		// knockback only for living creatures
2157 			{
2158 				// PMM - kickback was times 4 .. reduced to 3
2159 				// now this does no damage, just knockback
2160 				T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
2161 							/* self->dmg */ 0, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
2162 
2163 				if (!(other->flags & (FL_FLY|FL_SWIM)))
2164 					other->velocity[2] += 140;
2165 
2166 				damagetime = ((float)self->dmg)*FRAMETIME;
2167 				damagetime = damagetime / TRACKER_DAMAGE_TIME;
2168 //				gi.dprintf ("damage is %f\n", damagetime);
2169 
2170 				tracker_pain_daemon_spawn (self->owner, other, (int)damagetime);
2171 			}
2172 			else						// lots of damage (almost autogib) for dead bodies
2173 			{
2174 				T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
2175 							self->dmg*4, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
2176 			}
2177 		}
2178 		else	// full damage in one shot for inanimate objects
2179 		{
2180 			T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
2181 						self->dmg, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
2182 		}
2183 	}
2184 
2185 	tracker_explode (self, plane);
2186 	return;
2187 }
2188 
tracker_fly(edict_t * self)2189 void tracker_fly (edict_t *self)
2190 {
2191 	vec3_t	dest;
2192 	vec3_t	dir;
2193 	vec3_t	center;
2194 
2195 	if ((!self->enemy) || (!self->enemy->inuse) || (self->enemy->health < 1))
2196 	{
2197 		tracker_explode (self, NULL);
2198 		return;
2199 	}
2200 /*
2201 	VectorCopy (self->enemy->s.origin, dest);
2202 	if(self->enemy->client)
2203 		dest[2] += self->enemy->viewheight;
2204 */
2205 	// PMM - try to hunt for center of enemy, if possible and not client
2206 	if(self->enemy->client)
2207 	{
2208 		VectorCopy (self->enemy->s.origin, dest);
2209 		dest[2] += self->enemy->viewheight;
2210 	}
2211 	// paranoia
2212 	else if (VectorCompare(self->enemy->absmin, vec3_origin) || VectorCompare(self->enemy->absmax, vec3_origin))
2213 	{
2214 		VectorCopy (self->enemy->s.origin, dest);
2215 	}
2216 	else
2217 	{
2218 		VectorMA (vec3_origin, 0.5, self->enemy->absmin, center);
2219 		VectorMA (center, 0.5, self->enemy->absmax, center);
2220 		VectorCopy (center, dest);
2221 	}
2222 
2223 	VectorSubtract (dest, self->s.origin, dir);
2224 	VectorNormalize (dir);
2225 	vectoangles2 (dir, self->s.angles);
2226 	VectorScale (dir, self->speed, self->velocity);
2227 	VectorCopy(dest, self->monsterinfo.saved_goal);
2228 
2229 	self->nextthink = level.time + 0.1;
2230 }
2231 
fire_tracker(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,edict_t * enemy)2232 void fire_tracker (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, edict_t *enemy)
2233 {
2234 	edict_t	*bolt;
2235 	trace_t	tr;
2236 
2237 	VectorNormalize (dir);
2238 
2239 	bolt = G_Spawn();
2240 	VectorCopy (start, bolt->s.origin);
2241 	VectorCopy (start, bolt->s.old_origin);
2242 	vectoangles2 (dir, bolt->s.angles);
2243 	VectorScale (dir, speed, bolt->velocity);
2244 	bolt->movetype = MOVETYPE_FLYMISSILE;
2245 	bolt->clipmask = MASK_SHOT;
2246 	bolt->solid = SOLID_BBOX;
2247 	bolt->speed = speed;
2248 	bolt->s.effects = EF_TRACKER;
2249 	bolt->s.sound = gi.soundindex ("weapons/disrupt.wav");
2250 	VectorClear (bolt->mins);
2251 	VectorClear (bolt->maxs);
2252 
2253 	bolt->s.modelindex = gi.modelindex ("models/proj/disintegrator/tris.md2");
2254 	bolt->touch = tracker_touch;
2255 	bolt->enemy = enemy;
2256 	bolt->owner = self;
2257 	bolt->dmg = damage;
2258 	bolt->classname = "tracker";
2259 	gi.linkentity (bolt);
2260 
2261 	if(enemy)
2262 	{
2263 		bolt->nextthink = level.time + 0.1;
2264 		bolt->think = tracker_fly;
2265 	}
2266 	else
2267 	{
2268 		bolt->nextthink = level.time + 10;
2269 		bolt->think = G_FreeEdict;
2270 	}
2271 
2272 	if (self->client)
2273 		check_dodge (self, bolt->s.origin, dir, speed);
2274 
2275 	tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
2276 	if (tr.fraction < 1.0)
2277 	{
2278 		VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
2279 		bolt->touch (bolt, tr.ent, NULL, NULL);
2280 	}
2281 }
2282