1 #include "g_local.h"
2 #include "m_player.h"
3 
4 extern qboolean is_quad;
5 extern byte is_silenced;
6 
7 void playQuadSound(edict_t *ent);
8 void Weapon_Generic (edict_t *ent,
9 					 int FRAME_ACTIVATE_LAST,
10 					 int FRAME_FIRE_LAST,
11 					 int FRAME_IDLE_LAST,
12 					 int FRAME_DEACTIVATE_LAST,
13 					 int *pause_frames,
14 					 int *fire_frames,
15 					 void (*fire)(edict_t *ent));
16 void NoAmmoWeaponChange (edict_t *ent);
17 void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed);
18 
19 void Grenade_Explode(edict_t *ent);
20 void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result);
21 
22 
23 void fire_sconnan (edict_t *self);
24 void fire_sconnanEffects (edict_t *self);
25 
26 const int SC_MAXFIRETIME    = 5;         // in seconds...
27 const int SC_BASEDAMAGE     = 10;        // minimum damage
28 const int SC_DAMGERANGE     = 990;       // maximum damaged range (max damage possible is SC_BASEDAMAGE + SC_DAMGERANGE)
29 const int SC_MAXRADIUS      = 500;       // maximum blast radius
30 const int SC_MAXCELLS       = 100;       // maximum number of cells
31 
VectorLengthSquared(vec3_t v)32 vec_t VectorLengthSquared(vec3_t v)
33 {
34 	int		i;
35 	float	length;
36 
37 	length = 0;
38 	for (i=0 ; i< 3 ; i++)
39 		length += v[i]*v[i];
40 
41 	return length;
42 }
43 
angleToward(edict_t * self,vec3_t point,float speed)44 void angleToward(edict_t *self, vec3_t point, float speed)
45 {
46 	vec3_t forward;
47 	float yaw = 0.0;
48 	float vel = 0.0;
49 	vec3_t delta;
50 	vec3_t destAngles;
51 	VectorSubtract(point, self->s.origin, delta);
52 	vectoangles(delta, destAngles);
53 	self->ideal_yaw = destAngles[YAW];
54 	self->yaw_speed = speed;
55 	M_ChangeYaw(self);
56 	yaw = self->s.angles[YAW];
57 	self->ideal_yaw = destAngles[PITCH];
58 	self->s.angles[YAW] = self->s.angles[PITCH];
59 	M_ChangeYaw(self);
60 	self->s.angles[PITCH] = self->s.angles[YAW];
61 	self->s.angles[YAW] = yaw;
62 	AngleVectors (self->s.angles, forward, NULL, NULL);
63 	vel = VectorLength(self->velocity);
64 	VectorScale(forward, vel, self->velocity);
65 }
66 
67 #define MAXROTATION 20
68 
69 /*
70 	Laser Trip Bombs
71 */
72 // spawnflags
73 #define CHECK_BACK_WALL 1
74 
75 // variables
76 #define TBOMB_DELAY	1.0
77 #define TBOMB_TIMEOUT	180
78 #define TBOMB_DAMAGE 150
79 #define TBOMB_RADIUS_DAMAGE 384
80 #define TBOMB_HEALTH 100
81 #define TBOMB_SHRAPNEL	5
82 #define TBOMB_SHRAPNEL_DMG	15
83 #define TBOMB_MAX_EXIST	25
84 
shrapnel_touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)85 void shrapnel_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
86 {
87 	// do damage if we can
88 	if (!other->takedamage)
89 		return;
90 
91 	if (VectorCompare(ent->velocity, vec3_origin))
92 		return;
93 
94 	T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin,
95 		plane->normal, TBOMB_SHRAPNEL_DMG, 8, 0, MOD_TRIPBOMB);
96 	G_FreeEdict(ent);
97 }
98 
TripBomb_Explode(edict_t * ent)99 void TripBomb_Explode (edict_t *ent)
100 {
101 	vec3_t origin;
102 	int i = 0;
103 
104 	T_RadiusDamage(ent, ent->owner ? ent->owner : ent, ent->dmg, ent->enemy, ent->dmg_radius, MOD_TRIPBOMB);
105 
106 	VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
107 
108 	gi.WriteByte (svc_temp_entity);
109 	if (ent->waterlevel)
110 	{
111 		if (ent->groundentity)
112 			gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
113 		else
114 			gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
115 	}
116 	else
117 	{
118 		if (ent->groundentity)
119 			gi.WriteByte (TE_GRENADE_EXPLOSION);
120 		else
121 			gi.WriteByte (TE_ROCKET_EXPLOSION);
122 	}
123 	gi.WritePosition (origin);
124 	gi.multicast (ent->s.origin, MULTICAST_PHS);
125 
126 	// throw off some debris
127 	for (i = 0; i < TBOMB_SHRAPNEL; i++)
128 	{
129 		edict_t *sh = G_Spawn();
130 		vec3_t forward, right, up;
131 		sh->classname = "shrapnel";
132 		sh->movetype = MOVETYPE_BOUNCE;
133 		sh->solid = SOLID_BBOX;
134 		sh->s.effects |= EF_GRENADE;
135 		sh->s.modelindex = gi.modelindex("models/objects/shrapnel/tris.md2");
136 		sh->owner = ent->owner;
137 		VectorSet (sh->avelocity, 300, 300, 300);
138 		VectorCopy(ent->s.origin, sh->s.origin);
139 		AngleVectors (ent->s.angles, forward, right, up);
140 		VectorScale(forward, 500, forward);
141 		VectorMA(forward, crandom()*500, right, forward);
142 		VectorMA(forward, crandom()*500, up, forward);
143 		VectorCopy(forward, sh->velocity);
144 		sh->touch = shrapnel_touch;
145 		sh->think = G_FreeEdict;
146 		sh->nextthink = level.time + 3.0 + crandom() * 1.5;
147 	}
148 
149 	G_FreeEdict(ent);
150 }
151 
tripbomb_laser_think(edict_t * self)152 void tripbomb_laser_think (edict_t *self)
153 {
154 	vec3_t start;
155 	vec3_t end;
156 	vec3_t delta;
157 	trace_t	tr;
158 	int		count = 8;
159 
160 	self->nextthink = level.time + FRAMETIME;
161 
162 	if (level.time > self->timeout)
163 	{
164 		// play a sound
165 		//gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_trig.wav"), 1, ATTN_NORM, 0);
166 
167 		// blow up
168 		self->chain->think = TripBomb_Explode;
169 		self->chain->nextthink = level.time + FRAMETIME;
170 		G_FreeEdict(self);
171 		return;
172 	}
173 
174 	// randomly phase out or EMPNuke is in effect
175 	if (EMPNukeCheck(self, self->s.origin) || random() < 0.1)
176 	{
177 		self->svflags != SVF_NOCLIENT;
178 		return;
179 	}
180 
181 	self->svflags &= ~SVF_NOCLIENT;
182 	VectorCopy (self->s.origin, start);
183 	VectorMA (start, 2048, self->movedir, end);
184 	tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
185 
186 	if (!tr.ent)
187 		return;
188 
189 	VectorSubtract(tr.endpos, self->move_origin, delta);
190 	if (VectorCompare(self->s.origin, self->move_origin))
191 	{
192 		// we haven't done anything yet
193 		VectorCopy(tr.endpos, self->move_origin);
194 		if (self->spawnflags & 0x80000000)
195 		{
196 			self->spawnflags &= ~0x80000000;
197 			gi.WriteByte (svc_temp_entity);
198 			gi.WriteByte (TE_LASER_SPARKS);
199 			gi.WriteByte (count);
200 			gi.WritePosition (tr.endpos);
201 			gi.WriteDir (tr.plane.normal);
202 			gi.WriteByte (self->s.skinnum);
203 			gi.multicast (tr.endpos, MULTICAST_PVS);
204 		}
205 	}
206 	else if (VectorLength(delta) > 1.0)
207 	{
208 		// play a sound
209 		//gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_trig.wav"), 1, ATTN_NORM, 0);
210 
211 		// blow up
212 		self->chain->think = TripBomb_Explode;
213 		self->chain->nextthink = level.time + FRAMETIME;
214 		G_FreeEdict(self);
215 		return;
216 	}
217 	VectorCopy(self->move_origin, self->s.old_origin);
218 }
219 
tripbomb_laser_on(edict_t * self)220 void tripbomb_laser_on (edict_t *self)
221 {
222 	self->svflags &= ~SVF_NOCLIENT;
223 	self->think = tripbomb_laser_think;
224 
225 	// play a sound
226 	gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_arm.wav"), 1, ATTN_NORM, 0);
227 	tripbomb_laser_think(self);
228 	//gi.positioned_sound(self->s.old_origin, self, CHAN_AUTO, gi.soundindex("weapons/ired/las_tink.wav"), 1, ATTN_NORM, 0);
229 }
230 
create_tripbomb_laser(edict_t * bomb)231 void create_tripbomb_laser(edict_t *bomb)
232 {
233 	// create the laser
234 	edict_t *laser = G_Spawn();
235 	bomb->chain = laser;
236 	laser->classname = "laser trip bomb laser";
237 	VectorCopy(bomb->s.origin, laser->s.origin);
238 	VectorCopy(bomb->s.origin, laser->move_origin);
239 	VectorCopy(bomb->s.angles, laser->s.angles);
240 	G_SetMovedir (laser->s.angles, laser->movedir);
241 	laser->owner = bomb;
242 	laser->s.skinnum = 0xb0b1b2b3; // <- faint purple  0xf3f3f1f1 <-blue  red-> 0xf2f2f0f0;
243 	laser->s.frame = 2;
244 	laser->movetype = MOVETYPE_NONE;
245 	laser->solid = SOLID_NOT;
246 	laser->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
247 	laser->s.modelindex = 1;
248 	laser->chain = bomb;
249 	laser->spawnflags |= 0x80000001;
250 	laser->think = tripbomb_laser_on;
251 	laser->nextthink = level.time + FRAMETIME;
252 	laser->svflags |= SVF_NOCLIENT;
253 	laser->timeout = level.time + TBOMB_TIMEOUT;
254 	gi.linkentity (laser);
255 }
256 
use_tripbomb(edict_t * self,edict_t * other,edict_t * activator)257 void use_tripbomb(edict_t *self, edict_t *other, edict_t *activator)
258 {
259 	if (self->chain)
260 	{
261 		// we already have a laser, remove it
262 		G_FreeEdict(self->chain);
263 		self->chain = NULL;
264 	}
265 	else
266 		// create the laser
267 		create_tripbomb_laser(self);
268 }
269 
turnOffGlow(edict_t * self)270 void turnOffGlow(edict_t *self)
271 {
272 	self->s.effects &= ~EF_COLOR_SHELL;
273 	self->s.renderfx &= ~RF_SHELL_GREEN;
274 	self->think = NULL;
275 	self->nextthink = 0;
276 }
277 
tripbomb_pain(edict_t * self,edict_t * other,float kick,int damage)278 void tripbomb_pain(edict_t *self, edict_t *other, float kick, int damage)
279 {
280 	// play the green glow sound
281 	//gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_glow.wav"), 1, ATTN_NORM, 0);
282 
283 	// turn on the glow
284 	self->damage_debounce_time = level.time + 0.2;
285 
286 	// if we don't have a think function, then turn this thing on
287 	if (self->think == NULL)
288 	{
289 		self->s.effects |= EF_COLOR_SHELL;
290 		self->s.renderfx |= RF_SHELL_GREEN;
291 		self->nextthink = self->damage_debounce_time;
292 		self->think = turnOffGlow;
293 	}
294 }
295 
tripbomb_think(edict_t * self)296 void tripbomb_think(edict_t *self)
297 {
298 	if (self->chain == NULL)
299 	{
300 		// check whether we need to create the laser
301 		if (self->timeout < level.time)
302 		{
303 			create_tripbomb_laser(self);
304 		}
305 	}
306 
307 	// do we need to show damage?
308 	if (self->damage_debounce_time > level.time)
309 	{
310 		self->s.effects |= EF_COLOR_SHELL;
311 		self->s.renderfx |= RF_SHELL_GREEN;
312 	}
313 	else
314 	{
315 		self->s.effects &= ~EF_COLOR_SHELL;
316 		self->s.renderfx &= ~RF_SHELL_GREEN;
317 	}
318 
319 	self->nextthink = level.time + FRAMETIME;
320 }
321 
setupBomb(edict_t * bomb,char * classname,float damage,float damage_radius)322 void setupBomb(edict_t *bomb, char *classname, float damage, float damage_radius)
323 {
324 	bomb->classname = classname;
325 	VectorSet(bomb->mins, -8, -8, -8);
326 	VectorSet(bomb->maxs, 8, 8, 8);
327 	bomb->solid = SOLID_BBOX;
328 	bomb->movetype = MOVETYPE_NONE;
329 	bomb->s.modelindex = gi.modelindex("models/objects/ired/tris.md2");
330 	bomb->radius_dmg = damage;
331 	bomb->dmg = damage;
332 	bomb->dmg_radius = damage_radius;
333 	bomb->health = 1;
334 	bomb->takedamage = DAMAGE_IMMORTAL; // health will not be deducted
335 	bomb->pain = tripbomb_pain;
336 }
337 
removeOldest()338 void removeOldest()
339 {
340 	edict_t *oldestEnt = NULL;
341 	edict_t *e = NULL;
342 	int count = 0;
343 
344 	while(1)
345 	{
346 		e = G_Find(e, FOFS(classname), "ired");
347 		if (e == NULL) // no more
348 			break;
349 
350 		count++;
351 
352 		if (oldestEnt == NULL ||
353 			e->timestamp < oldestEnt->timestamp)
354 		{
355 			oldestEnt = e;
356 		}
357 	}
358 
359 	// do we have too many?
360 	if (count > TBOMB_MAX_EXIST && oldestEnt != NULL)
361 	{
362 		// get this tbomb to explode
363 		oldestEnt->think = TripBomb_Explode;
364 		oldestEnt->nextthink = level.time + FRAMETIME;
365 		G_FreeEdict(oldestEnt->chain);
366 	}
367 }
368 
fire_lasertripbomb(edict_t * self,vec3_t start,vec3_t dir,float timer,float damage,float damage_radius,qboolean quad)369 qboolean fire_lasertripbomb(edict_t *self, vec3_t start, vec3_t dir, float timer, float damage, float damage_radius, qboolean quad)
370 {
371 	// trace a line
372 	trace_t tr;
373 	vec3_t endPos;
374 	vec3_t _dir;
375 	edict_t *bomb = NULL;
376 	edict_t *laser = NULL;
377 
378 	VectorScale(dir, 64, _dir);
379 	VectorAdd(start, _dir, endPos);
380 
381 	// trace ahead, looking for a wall
382 	tr = gi.trace(start, NULL, NULL, endPos, self, MASK_SHOT);
383 	if (tr.fraction == 1.0)
384 	{
385 		// not close enough
386 		//gi.cprintf(self, PRINT_HIGH, "Not close enough to a wall");
387 		return false;
388 	}
389 
390 	if (Q_stricmp(tr.ent->classname, "worldspawn") != 0)
391 	{
392 		//gi.cprintf(self, PRINT_HIGH, "Hit something other than a wall");
393 		return false;
394 	}
395 
396 	// create the bomb
397 	bomb = G_Spawn();
398 	//VectorCopy(tr.endpos, bomb->s.origin);
399 	VectorMA(tr.endpos, 3, tr.plane.normal, bomb->s.origin);
400 	vectoangles(tr.plane.normal, bomb->s.angles);
401 	bomb->owner = self;
402 	setupBomb(bomb, "ired", damage, damage_radius);
403 	gi.linkentity(bomb);
404 
405 	bomb->timestamp = level.time;
406 	bomb->timeout = level.time + timer;
407 	bomb->nextthink = level.time + FRAMETIME;
408 	bomb->think = tripbomb_think;
409 
410 	// remove the oldest trip bomb
411 	removeOldest();
412 
413 	// play a sound
414 	gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_set.wav"), 1, ATTN_NORM, 0);
415 	return true;
416 }
417 
weapon_lasertripbomb_fire(edict_t * ent)418 void weapon_lasertripbomb_fire (edict_t *ent)
419 {
420 	if (ent->client->ps.gunframe == 10)
421 	{
422 		vec3_t	offset;
423 		vec3_t	forward;
424 		vec3_t	start;
425 		int damage = TBOMB_DAMAGE;
426 		float radius = TBOMB_RADIUS_DAMAGE;
427 		if (is_quad)
428 			damage *= 4;
429 
430 		// place the trip bomb
431 		VectorSet(offset, 0, 0, ent->viewheight * 0.75);
432 		AngleVectors (ent->client->v_angle, forward, NULL, NULL);
433 		VectorAdd(ent->s.origin, offset, start);
434 
435 		if (fire_lasertripbomb(ent, start, forward, TBOMB_DELAY, damage, radius, is_quad))
436 		{
437 			ent->client->pers.inventory[ent->client->ammo_index] -= 1;
438 
439 			// switch models
440 			ent->client->ps.gunindex = gi.modelindex("models/weapons/v_ired/hand.md2");
441 
442 			// play quad sound
443 			playQuadSound(ent);
444 		}
445 	}
446 	else if (ent->client->ps.gunframe == 15)
447 	{
448 		// switch models back
449 		int mi = gi.modelindex("models/weapons/v_ired/tris.md2");
450 		if (ent->client->ps.gunindex != mi)
451 		{
452 			ent->client->ps.gunindex = mi;
453 			// go back to get another trip bomb
454 			ent->client->ps.gunframe = 0;
455 			return;
456 		}
457 	}
458 	else if (ent->client->ps.gunframe == 6)
459 	{
460 		ent->client->ps.gunframe = 16;
461 		return;
462 	}
463 
464 	ent->client->ps.gunframe++;
465 }
466 
Weapon_LaserTripBomb(edict_t * ent)467 void Weapon_LaserTripBomb(edict_t *ent)
468 {
469 	static int	pause_frames[]	= {24, 33, 43, 0};
470 	static int	fire_frames[]	= {6, 10, 15, 0};
471 
472 	const int deactivateFirst = 44;
473 	const int deactivateLast = 48;
474 	const int idleFirst = 16;
475 	const int idleLast = 43;
476 	const int fireFirst = 7;
477 	const int fireLast = 15;
478 	const int activateFirst = 0;
479 	const int activateLast = 6;
480 
481 	if (ent->client->weaponstate == WEAPON_DROPPING)
482 	{
483 		if (ent->client->ps.gunframe == deactivateLast)
484 		{
485 			ChangeWeapon (ent);
486 			return;
487 		}
488 
489 		ent->client->ps.gunframe++;
490 		return;
491 	}
492 
493 	if (ent->client->weaponstate == WEAPON_ACTIVATING)
494 	{
495 		if (ent->client->ps.gunframe == activateLast)
496 		{
497 			ent->client->weaponstate = WEAPON_READY;
498 			ent->client->ps.gunframe = idleFirst;
499 			return;
500 		}
501 
502 		ent->client->ps.gunframe++;
503 		return;
504 	}
505 
506 	if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING))
507 	{
508 		ent->client->weaponstate = WEAPON_DROPPING;
509 		ent->client->ps.gunframe = deactivateFirst;
510 		return;
511 	}
512 
513 	if (ent->client->weaponstate == WEAPON_READY)
514 	{
515 		if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) )
516 		{
517 			ent->client->latched_buttons &= ~BUTTON_ATTACK;
518 			if(ent->client->pers.inventory[ent->client->ammo_index])
519 			{
520 				ent->client->ps.gunframe = fireFirst;
521 				ent->client->weaponstate = WEAPON_FIRING;
522 
523 				// start the animation
524 				ent->client->anim_priority = ANIM_ATTACK;
525 				if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
526 				{
527 					ent->s.frame = FRAME_crattak1-1;
528 					ent->client->anim_end = FRAME_crattak9;
529 				}
530 				else
531 				{
532 					ent->s.frame = FRAME_attack1-1;
533 					ent->client->anim_end = FRAME_attack8;
534 				}
535 			}
536 			else
537 			{
538 				if (level.time >= ent->pain_debounce_time)
539 				{
540 					gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
541 					ent->pain_debounce_time = level.time + 1;
542 				}
543 				NoAmmoWeaponChange (ent);
544 			}
545 		}
546 		else
547 		{
548 			if (ent->client->ps.gunframe == idleLast)
549 			{
550 				ent->client->ps.gunframe = idleFirst;
551 				return;
552 			}
553 
554 			if (pause_frames)
555 			{
556 				int n = 0;
557 				for (n = 0; pause_frames[n]; n++)
558 				{
559 					if (ent->client->ps.gunframe == pause_frames[n])
560 					{
561 						if (rand()&15)
562 							return;
563 					}
564 				}
565 			}
566 
567 			ent->client->ps.gunframe++;
568 			return;
569 		}
570 	}
571 
572 	if (ent->client->weaponstate == WEAPON_FIRING)
573 	{
574 		int n = 0;
575 		for (n = 0; fire_frames[n]; n++)
576 		{
577 			if (ent->client->ps.gunframe == fire_frames[n])
578 			{
579 				weapon_lasertripbomb_fire(ent);
580 				break;
581 			}
582 		}
583 
584 		if (!fire_frames[n])
585 			ent->client->ps.gunframe++;
586 
587 		if (ent->client->ps.gunframe == idleFirst+1)
588 			ent->client->weaponstate = WEAPON_READY;
589 	}
590 }
591 
SP_misc_lasertripbomb(edict_t * bomb)592 void SP_misc_lasertripbomb(edict_t *bomb)
593 {
594 	// precache
595 	gi.soundindex("weapons/ired/las_set.wav");
596 	gi.soundindex("weapons/ired/las_arm.wav");
597 	//gi.soundindex("weapons/ired/las_tink.wav");
598 	//gi.soundindex("weapons/ired/las_trig.wav");
599 	//gi.soundindex("weapons/ired/las_glow.wav");
600 	gi.modelindex("models/objects/shrapnel/tris.md2");
601 	gi.modelindex("models/objects/ired/tris.md2");
602 
603 	if (bomb->spawnflags & CHECK_BACK_WALL)
604 	{
605 		vec3_t forward, endPos;
606 		trace_t tr;
607 		// look backwards toward a wall
608 		AngleVectors (bomb->s.angles, forward, NULL, NULL);
609 		VectorMA(bomb->s.origin, -64.0, forward, endPos);
610 		tr = gi.trace(bomb->s.origin, NULL, NULL, endPos, bomb, MASK_SOLID);
611 		VectorCopy(tr.endpos, bomb->s.origin);
612 		vectoangles(tr.plane.normal, bomb->s.angles);
613 	}
614 
615 	// set up ourself
616 	setupBomb(bomb, "misc_ired", TBOMB_DAMAGE, TBOMB_RADIUS_DAMAGE);
617 
618 	if (bomb->targetname)
619 	{
620 		bomb->use = use_tripbomb;
621 	}
622 	else
623 	{
624 		bomb->think = create_tripbomb_laser;
625 		bomb->nextthink = level.time + TBOMB_DELAY;
626 	}
627 	gi.linkentity(bomb);
628 }
629 
630 /*
631 ======================================================================
632 
633 Sonic Cannon
634 
635 ======================================================================
636 */
637 
638 
weapon_sc_fire(edict_t * ent)639 void weapon_sc_fire (edict_t *ent)
640 {
641 	if (!(ent->client->buttons & BUTTON_ATTACK))
642 	{
643 		ent->client->ps.gunframe++;
644 
645     if(ent->client->weapon_sound && ent->client->ps.gunframe < 18)
646     {
647       ent->client->ps.gunframe = 18;
648     }
649 	}
650 	else
651 	{
652 		if(EMPNukeCheck(ent, ent->s.origin))
653 		{
654 			gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
655 
656 			ent->client->ps.gunframe = 18;
657 			ent->client->weapon_sound = 0;
658 			ent->weaponsound_time = 0;
659 
660 	    ent->dmg_radius = 0;
661 		  ent->client->startFireTime = 0;
662 			return;
663 		}
664 
665     if(!ent->client->startFireTime)
666     {
667       ent->client->startFireTime = level.time;
668     }
669     else if(level.time - ent->client->startFireTime >= SC_MAXFIRETIME)
670     {
671       ent->client->ps.gunframe = 17;
672     }
673     else
674     {
675       int old_cells = (int)ent->dmg_radius;
676       ent->dmg_radius = ((level.time - ent->client->startFireTime) /  SC_MAXFIRETIME) * SC_MAXCELLS;
677 
678       if(old_cells < (int)ent->dmg_radius)
679       {
680         old_cells = (int)ent->dmg_radius - old_cells;
681 
682 
683         if(ent->client->pers.inventory[ent->client->ammo_index] < old_cells)
684         {
685           ent->dmg_radius -= (old_cells - ent->client->pers.inventory[ent->client->ammo_index]);
686           ent->client->pers.inventory[ent->client->ammo_index] = 0;
687         }
688         else
689         {
690           ent->client->pers.inventory[ent->client->ammo_index] -= old_cells;
691         }
692       }
693     }
694 
695     if(!ent->client->pers.inventory[ent->client->ammo_index])
696     {
697       ent->client->ps.gunframe = 17;
698 
699 			if (level.time >= ent->pain_debounce_time)
700 			{
701 				gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
702 				ent->pain_debounce_time = level.time + 1;
703 			}
704 			NoAmmoWeaponChange (ent);
705 		}
706 		else
707 		{
708       if(ent->weaponsound_time < level.time)
709       {
710 	      ent->client->weapon_sound = gi.soundindex("weapons/sonic/sc_fire.wav");
711       }
712 		}
713 
714     fire_sconnanEffects (ent);
715 
716 		ent->client->ps.gunframe++;
717 		if (ent->client->ps.gunframe == 18 && (level.time - ent->client->startFireTime) < SC_MAXFIRETIME && ent->client->pers.inventory[ent->client->ammo_index])
718 			ent->client->ps.gunframe = 12;
719 	}
720 
721 	if (ent->client->ps.gunframe == 18)
722 	{
723 		ent->client->weapon_sound = 0;
724     ent->weaponsound_time = 0;
725 
726 		if(EMPNukeCheck(ent, ent->s.origin))
727 		{
728 			gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
729 		}
730 		else
731 		{
732 			if (!is_silenced)
733 				gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_cool.wav"), 1, ATTN_NORM, 0);
734 			else
735 				gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_cool.wav"), 0.4, ATTN_NORM, 0);
736 
737 			if(ent->dmg_radius)
738 			{
739 				fire_sconnan (ent);
740 			}
741 		}
742 
743     ent->dmg_radius = 0;
744     ent->client->startFireTime = 0;
745 	}
746 }
747 
748 
749 
750 
Weapon_SonicCannon(edict_t * ent)751 void Weapon_SonicCannon (edict_t *ent)
752 {
753 	static int	pause_frames[] = {32, 42, 52, 0};
754 	static int	fire_frames[]	= {12, 13, 14, 15, 16, 17, 0};
755 
756 	if (ent->client->ps.gunframe == 0)
757 	{
758 		if (deathmatch->value)
759 		{
760 			if (!is_silenced)
761 				gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_act.wav"), 1, ATTN_NORM, 0);
762 			else
763 				gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_act.wav"), 0.4, ATTN_NORM, 0);
764 		}
765     ent->weaponsound_time = 0;
766     ent->client->startFireTime = 0;
767     ent->dmg_radius = 0;
768   }
769   else if (ent->client->ps.gunframe == 53)
770   {
771 		if (deathmatch->value)
772 		{
773 			if (!is_silenced)
774 				gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_dact.wav"), 1, ATTN_NORM, 0);
775 			else
776 				gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_dact.wav"), 0.4, ATTN_NORM, 0);
777 		}
778   }
779   else if((ent->client->buttons & BUTTON_ATTACK) && ent->weaponsound_time == 0)
780   {
781     ent->weaponsound_time = level.time + 0.4;
782 
783 		if (!is_silenced)
784 			gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_warm.wav"), 1, ATTN_NORM, 0);
785 		else
786 			gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_warm.wav"), 0.4, ATTN_NORM, 0);
787   }
788 
789   Weapon_Generic (ent, 6, 22, 52, 57, pause_frames, fire_frames, weapon_sc_fire);
790 }
791 
792 
793 
794 void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage);
795 
fire_sconnanEffects(edict_t * self)796 void fire_sconnanEffects (edict_t *self)
797 {
798 	vec3_t		start, end;
799 	vec3_t		forward, right;
800 	vec3_t		offset, v;
801 	trace_t		tr;
802 
803 	AngleVectors (self->client->v_angle, forward, right, NULL);
804 
805 	VectorScale (forward, -3, self->client->kick_origin);
806 	self->client->kick_angles[0] = -3;
807 
808 	VectorSet(offset, 0, 7,  self->viewheight-8);
809 	P_ProjectSource (self->client, self->s.origin, offset, forward, right, start);
810 
811 	VectorMA (start, 8192, forward, end);
812 
813   tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA);
814 
815 	VectorMA (tr.endpos, -5, forward, end);
816 
817   VectorSet(v, crandom() * 10 - 20, crandom() * 10 - 20, crandom() * 10 - 20);
818   SpawnDamage(TE_SHIELD_SPARKS, end, v, 0);
819 }
820 
821 
822 
scexplode_think(edict_t * self)823 void scexplode_think(edict_t *self)
824 {
825   gi.WriteByte (svc_temp_entity);
826 	gi.WriteByte (TE_ROCKET_EXPLOSION);
827 	gi.WritePosition (self->s.origin);
828 	gi.multicast (self->s.origin, MULTICAST_PHS);
829 
830 	G_FreeEdict (self);
831 }
832 
833 
fire_sconnan(edict_t * self)834 void fire_sconnan (edict_t *self)
835 {
836 	vec3_t		start, end, explodepos;
837 	vec3_t		forward, right, up;
838 	vec3_t		offset;
839 	trace_t		tr;
840   float damage;
841   float radius;
842 
843   damage = self->dmg_radius / SC_MAXCELLS;
844   radius = damage * SC_MAXRADIUS;
845   damage = SC_BASEDAMAGE + (damage * SC_DAMGERANGE);
846 
847 	AngleVectors (self->client->v_angle, forward, right, up);
848 
849 	VectorScale (forward, -3, self->client->kick_origin);
850 	self->client->kick_angles[0] = -3;
851 
852 	VectorSet(offset, 0, 7,  self->viewheight-8);
853 	P_ProjectSource (self->client, self->s.origin, offset, forward, right, start);
854 
855 	VectorMA (start, 8192, forward, end);
856 
857   tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA);
858 
859   if ((tr.ent != self) && (tr.ent->takedamage))
860   {
861 		T_Damage (tr.ent, self, self, forward, tr.endpos, tr.plane.normal, damage, 0, 0, MOD_SONICCANNON);
862   }
863 
864   T_RadiusDamagePosition (tr.endpos, self, self, damage, tr.ent, radius, MOD_SONICCANNON);
865 
866 	VectorMA (tr.endpos, -5, forward, end);
867 
868   gi.WriteByte (svc_temp_entity);
869 	gi.WriteByte (TE_ROCKET_EXPLOSION);
870 	gi.WritePosition (end);
871 	gi.multicast (self->s.origin, MULTICAST_PHS);
872 
873   damage -= 100;
874   radius = 0.1;
875 
876   while(damage > 0)
877   {
878 	  edict_t	*explode;
879 
880     VectorMA (end, (50 * crandom()) - 5, forward, explodepos);
881   	VectorMA (explodepos, (50 * crandom()) - 5, right, explodepos);
882   	VectorMA (explodepos, (50 * crandom()) - 5, up, explodepos);
883 
884     explode = G_Spawn();
885   	VectorCopy (explodepos, explode->s.origin);
886 
887   	explode->classname = "sconnanExplode";
888   	explode->nextthink = level.time + radius;
889   	explode->think = scexplode_think;
890 
891     radius += 0.1;
892     damage -= 100;
893   }
894 
895 	// play quad damage sound
896 	playQuadSound(self);
897 }
898 
899 
900 
901 
902 /*
903 	Flares
904 */
905 #define FLASH_RANGE		256.0
906 void FoundTarget (edict_t *self);
907 
flare_flash(edict_t * ent)908 void flare_flash(edict_t *ent)
909 {
910 	edict_t *target = NULL;
911 
912 	// flash
913 	while (1)
914 	{
915 		float dist;
916 		float ratio;
917 		float dot;
918 		vec3_t delta;
919 		vec3_t forward;
920 
921 		// get the next entity near us
922 		target = findradius(target, ent->s.origin, FLASH_RANGE);
923 		if (target == NULL)
924 			break;
925 		if (!target->client && !(target->svflags & SVF_MONSTER))
926 			continue;
927 		if (target->deadflag)
928 			continue;
929 		if (!visible(ent, target))
930 			continue;
931 		//if (!infront(target, ent))
932 		//	continue;
933 
934 		// what's the distance, so that closer get's more
935 		VectorSubtract(ent->s.origin, target->s.origin, delta);
936 		dist = VectorLength(delta);
937 		ratio = 1 - (dist/FLASH_RANGE);
938 		if (ratio < 0)
939 			ratio = 0;
940 
941 		// looking to the side get's less
942 		AngleVectors(target->s.angles, forward, NULL, NULL);
943 		VectorNormalize(delta);
944 		dot = Z_MAX(0.0, DotProduct(delta, forward));
945 		ratio *= dot;// * 1.25;
946 
947 		// set the flash counter
948 		if (target->client)
949 		{
950 			target->client->flashTime += ratio*25;
951 			if (target->client->flashTime > 25)
952 				target->client->flashTime = 25;
953 			target->client->flashBase = 30;
954 
955 			if (deathmatch->value &&
956 				!target->client->pers.gl_polyblend &&
957 				!(((int)zdmflags->value) & ZDM_NO_GL_POLYBLEND_DAMAGE))
958 				T_Damage(target, ent, ent->owner, vec3_origin, target->s.origin, vec3_origin, (int)(10.0*ratio), 0, 0, MOD_GL_POLYBLEND);
959 		}
960 		else if ((target->svflags & SVF_MONSTER) && strcmp(target->classname, "monster_zboss") != 0)
961 		{
962 			target->monsterinfo.flashTime =
963 				Z_MAX(target->monsterinfo.flashTime, ratio*150); // a little bit more advantageous
964 			target->monsterinfo.flashBase = 50;
965 			if (target->enemy == NULL)
966 			{
967 				target->enemy = ent->owner;
968 				FoundTarget(target);
969 			}
970 		}
971 	}
972 }
973 
flare_think(edict_t * self)974 void flare_think(edict_t *self)
975 {
976 	edict_t *target = NULL;
977 	edict_t *closestEnt = NULL;
978 	float closestDist = 0.0;
979 
980 	// on our last leg?
981 	if (level.time > self->timeout)
982 	{
983 		self->s.effects &= ~EF_ROCKET;
984 		self->think = G_FreeEdict;
985 		self->nextthink = level.time + 4.0;
986 		self->s.frame = 0;
987 		self->s.sound = 0;
988 		return;
989 	}
990 
991 	self->s.frame++;
992 
993 	if (self->s.frame > 14)
994 		self->s.frame = 5;
995 
996 	// hissing sound
997 	self->s.sound = gi.soundindex ("weapons/flare/flarehis.wav");
998 
999 	// do the visual thing
1000 	flare_flash(self);
1001 
1002 	// next frame
1003 	self->nextthink = level.time + FRAMETIME;
1004 }
1005 
fire_flare(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,float damage_radius,int radius_damage)1006 void fire_flare (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
1007 {
1008 	edict_t	*flare;
1009 	vec3_t adir;
1010 	vec3_t up;
1011 
1012 	vectoangles (dir, adir);
1013 	AngleVectors (adir, NULL, NULL, up);
1014 
1015 	flare = G_Spawn();
1016 	VectorCopy (start, flare->s.origin);
1017 	VectorCopy (dir, flare->movedir);
1018 	vectoangles (dir, flare->s.angles);
1019 	VectorScale (dir, speed, flare->velocity);
1020 	VectorMA (flare->velocity, 200 + crandom() * 10.0, up, flare->velocity);
1021 	flare->movetype = MOVETYPE_BOUNCE;
1022 	flare->clipmask = MASK_SHOT;
1023 	flare->solid = SOLID_BBOX;
1024 	flare->s.effects = EF_ROCKET;
1025 	VectorSet(flare->mins, -4, -4, -4);
1026 	VectorSet(flare->maxs, 4, 4, 4);
1027 	flare->s.modelindex = gi.modelindex("models/objects/flare/tris.md2");
1028 	flare->owner = self;
1029 	flare->timeout = level.time + 8000/speed;
1030 	flare->nextthink = level.time + 1.0;
1031 	flare->think = flare_think;
1032 	flare->dmg = damage;
1033 	flare->radius_dmg = radius_damage;
1034 	flare->dmg_radius = damage_radius;
1035 	flare->classname = "flare";
1036 
1037 	if (self->client)
1038 		check_dodge (self, flare->s.origin, dir, speed);
1039 
1040 	gi.linkentity (flare);
1041 }
1042 
Weapon_FlareLauncher_Fire(edict_t * ent)1043 void Weapon_FlareLauncher_Fire (edict_t *ent)
1044 {
1045 	vec3_t	offset, start;
1046 	vec3_t	forward, right;
1047 
1048 	AngleVectors (ent->client->v_angle, forward, right, NULL);
1049 
1050 	VectorSet(offset, 8, 8, ent->viewheight-8);
1051 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
1052 	fire_flare(ent, start, forward, 1, 600, 1, 1);
1053 
1054 	ent->client->ps.gunframe++;
1055 
1056 	PlayerNoise(ent, start, PNOISE_WEAPON);
1057 
1058 	// play quad sound
1059 	playQuadSound(ent);
1060 
1061 	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
1062 		ent->client->pers.inventory[ent->client->ammo_index]--;
1063 
1064 	// play shooting sound
1065 	if (!is_silenced)
1066 		gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/flare/shoot.wav"), 1, ATTN_NORM, 0);
1067 	else
1068 		gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/flare/shoot.wav"), 0.4, ATTN_NORM, 0);
1069 }
1070 
Weapon_FlareGun(edict_t * ent)1071 void Weapon_FlareGun (edict_t *ent)
1072 {
1073 	static int	pause_frames[]	= {15, 25, 35, 0};
1074 	static int	fire_frames[]	= {8, 0};
1075 
1076 	Weapon_Generic (ent, 5, 14, 44, 48, pause_frames, fire_frames, Weapon_FlareLauncher_Fire);
1077 }
1078 
1079 /******************************
1080 	Sniper Rifle
1081 */
fire_sniper_bullet(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int kick)1082 void fire_sniper_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
1083 {
1084 	trace_t tr;
1085 	vec3_t end;
1086 	vec3_t s;
1087 	edict_t *ignore = self;
1088 	VectorMA (start, 8192, aimdir, end);
1089 	VectorCopy(start, s);
1090 	while(1)
1091 	{
1092 		tr = gi.trace (s, NULL, NULL, end, ignore, MASK_SHOT_NO_WINDOW);
1093 		if (tr.fraction >= 1.0)
1094 			return;
1095 
1096 		// if we hit a plasmashield, then pass thru it
1097 		if (Q_stricmp(tr.ent->classname, "PlasmaShield") == 0)
1098 		{
1099 			ignore = tr.ent;
1100 			VectorCopy(tr.endpos, s);
1101 		}
1102 		else
1103 			break;
1104 	}
1105 
1106 	gi.WriteByte (svc_temp_entity);
1107 	if (gi.pointcontents(tr.endpos) & MASK_WATER)
1108 	{
1109 		if (tr.plane.normal[2] > 0.7)
1110 			gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
1111 		else
1112 			gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
1113 	}
1114 	else
1115 	{
1116 		if (tr.plane.normal[2] > 0.7)
1117 			gi.WriteByte (TE_GRENADE_EXPLOSION);
1118 		else
1119 			gi.WriteByte (TE_ROCKET_EXPLOSION);
1120 	}
1121 	gi.WritePosition (tr.endpos);
1122 	gi.multicast (tr.endpos, MULTICAST_PHS);
1123 
1124 	if (tr.ent->takedamage)
1125 		T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_NO_ARMOR, MOD_SNIPERRIFLE);
1126 }
1127 
weapon_sniperrifle_fire(edict_t * ent)1128 void weapon_sniperrifle_fire (edict_t *ent)
1129 {
1130 	vec3_t forward, right;
1131 	vec3_t offset, start;
1132 	int damage;
1133 	int kick;
1134 
1135 	if (deathmatch->value)
1136 	{	// normal damage is too extreme in dm
1137 		damage = 150;
1138 		kick = 300;
1139 	}
1140 	else
1141 	{
1142 		damage = 250;
1143 		kick = 400;
1144 	}
1145 
1146 	if (is_quad)
1147 	{
1148 		damage *= 4;
1149 		kick *= 4;
1150 	}
1151 
1152 	AngleVectors (ent->client->v_angle, forward, right, NULL);
1153 	// centre the shot
1154 	VectorSet(offset, 0, 0, ent->viewheight);
1155 	VectorAdd(ent->s.origin, offset, start);
1156 	fire_sniper_bullet(ent, start, forward, damage, kick);
1157 
1158 	if (!is_silenced)
1159 		gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/fire.wav"), 1, ATTN_NORM, 0);
1160 	else
1161 		gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/fire.wav"), 0.4, ATTN_NORM, 0);
1162 
1163   	PlayerNoise(ent, start, PNOISE_WEAPON);
1164 
1165 	// play quad sound
1166 	playQuadSound(ent);
1167 
1168 	VectorScale (forward, -20, ent->client->kick_origin);
1169 	ent->client->kick_angles[0] = -2;
1170 	ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity;
1171 }
1172 
1173 #define SNIPER_CHARGE_TIME	30
Weapon_SniperRifle(edict_t * ent)1174 void Weapon_SniperRifle(edict_t *ent)
1175 {
1176 	/*
1177 		Activate/Deactivate
1178 		0 - 8	: Activate
1179 		9 - 18	: Fire
1180 		19 - 27 : Idle 1
1181 		28 - 36	: Idle 2
1182 		37 - 41	: Deactivate
1183 
1184 		Zoom
1185 		0 - 1 Zoom
1186 		Hold 1 while zoomed
1187 	*/
1188 	const static int activateStart = 0;
1189 	const static int activateEnd = 8;
1190 	const static int deactivateStart = 37;
1191 	const static int deactivateEnd = 41;
1192 	const static int spFov = 15;
1193 	const static int dmFov = 30;
1194 
1195 	if (ent->client->weaponstate == WEAPON_DROPPING)
1196 	{
1197 		ent->client->sniperFramenum = 0;
1198 		if (ent->client->ps.gunframe == deactivateStart)
1199 		{
1200 			// back to old fov
1201 			ent->client->ps.fov = 90;
1202 			if (deathmatch->value)
1203 				gi.sound(ent, CHAN_WEAPON2, gi.soundindex("weapons/sniper/snip_bye.wav"), 1, ATTN_NORM, 0);
1204 		}
1205 		else if (ent->client->ps.gunframe == deactivateEnd)
1206 		{
1207 			ChangeWeapon(ent);
1208 			return;
1209 		}
1210 
1211 		ent->client->ps.gunframe++;
1212 		return;
1213 	}
1214 
1215 	if (ent->client->weaponstate == WEAPON_ACTIVATING)
1216 	{
1217 		if (ent->client->ps.gunframe == activateStart)
1218 		{
1219 			// play the activation sound
1220 			if (deathmatch->value)
1221 				gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/snip_act.wav"), 1, ATTN_NORM, 0);
1222 		}
1223 		else if (ent->client->ps.gunframe == activateEnd)
1224 		{
1225 			ent->client->weaponstate = WEAPON_READY;
1226 			ent->client->ps.gunindex = (deathmatch->value ?
1227 				gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") :
1228 				gi.modelindex("models/weapons/v_sniper/scope/tris.md2") );
1229 			ent->client->ps.gunframe = 0;
1230 			ent->client->ps.fov = (deathmatch->value ? dmFov : spFov);
1231 			ent->client->sniperFramenum = level.framenum + SNIPER_CHARGE_TIME;
1232 			return;
1233 		}
1234 
1235 		ent->client->ps.gunframe++;
1236 		return;
1237 	}
1238 
1239 	if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING))
1240 	{
1241 		// back to other gun model
1242 		ent->client->ps.gunindex = gi.modelindex("models/weapons/v_sniper/tris.md2");
1243 		ent->client->weaponstate = WEAPON_DROPPING;
1244 		ent->client->ps.gunframe = deactivateStart;
1245 		return;
1246 	}
1247 
1248 	if (ent->client->weaponstate == WEAPON_READY)
1249 	{
1250 		ent->client->ps.gunindex = (deathmatch->value ?
1251 			gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") :
1252 			gi.modelindex("models/weapons/v_sniper/scope/tris.md2") );
1253 
1254 		ent->client->ps.fov = (deathmatch->value ? dmFov : spFov);
1255 
1256 		// beep if the sniper frame num is a multiple of 10
1257 		if (ent->client->sniperFramenum >= level.framenum)
1258 		{
1259 			if ((ent->client->sniperFramenum - level.framenum) % 10 == 1)
1260 				gi.sound(ent, CHAN_WEAPON2, gi.soundindex("weapons/sniper/beep.wav"), 1, ATTN_NORM, 0);
1261 		}
1262 
1263 		if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) )
1264 		{
1265 			if (level.framenum >= ent->client->sniperFramenum)
1266 			{
1267 				ent->client->latched_buttons &= ~BUTTON_ATTACK;
1268 				if ((!ent->client->ammo_index) ||
1269 					( ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity))
1270 				{
1271 					ent->client->weaponstate = WEAPON_FIRING;
1272 
1273 					// start the animation
1274 					ent->client->anim_priority = ANIM_ATTACK;
1275 					if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
1276 					{
1277 						ent->s.frame = FRAME_crattak1-1;
1278 						ent->client->anim_end = FRAME_crattak9;
1279 					}
1280 					else
1281 					{
1282 						ent->s.frame = FRAME_attack1-1;
1283 						ent->client->anim_end = FRAME_attack8;
1284 					}
1285 				}
1286 				else
1287 				{
1288 					if (level.time >= ent->pain_debounce_time)
1289 					{
1290 						gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
1291 						ent->pain_debounce_time = level.time + 1;
1292 					}
1293 					NoAmmoWeaponChange (ent);
1294 				}
1295 			}
1296 		}
1297 	}
1298 
1299 	if (ent->client->weaponstate == WEAPON_FIRING)
1300 	{
1301 		ent->client->ps.gunindex = (deathmatch->value ?
1302 				gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") :
1303 				gi.modelindex("models/weapons/v_sniper/scope/tris.md2") );
1304 
1305 		ent->client->ps.fov = (deathmatch->value ? dmFov : spFov);
1306 		//if (ent->client->quad_framenum > level.framenum)
1307 		//	gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
1308 
1309 		// fire
1310 		weapon_sniperrifle_fire(ent);
1311 
1312 		// start recharge
1313 		ent->client->weaponstate = WEAPON_READY;
1314 		ent->client->sniperFramenum = level.framenum + SNIPER_CHARGE_TIME;
1315 	}
1316 }
1317 
1318 /*****************************
1319 	Armageddon 2000
1320 */
1321 
weapon_a2k_exp_think(edict_t * self)1322 void weapon_a2k_exp_think(edict_t *self)
1323 {
1324 	self->s.frame++;
1325 	self->s.skinnum++;
1326 
1327 	if (self->s.frame == 6)
1328 	{
1329 		G_FreeEdict(self);
1330 		return;
1331 	}
1332 	self->nextthink = level.time + FRAMETIME;
1333 }
1334 
Z_RadiusDamageVisible(edict_t * inflictor,edict_t * attacker,float damage,edict_t * ignore,float radius,int mod)1335 void Z_RadiusDamageVisible(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
1336 {
1337 	float	points;
1338 	edict_t	*ent = NULL;
1339 	vec3_t	v;
1340 	vec3_t	dir;
1341 
1342 	while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
1343 	{
1344 		if (ent == ignore)
1345 			continue;
1346 		if (!ent->takedamage)
1347 			continue;
1348 		if (!visible(inflictor, ent))
1349 			continue;
1350 
1351 		VectorAdd (ent->mins, ent->maxs, v);
1352 		VectorMA (ent->s.origin, 0.5, v, v);
1353 		VectorSubtract (inflictor->s.origin, v, v);
1354 		points = damage - 0.5 * VectorLength (v);
1355 		if (ent == attacker)
1356 			points = points * 0.5;
1357 		if (points > 0)
1358 		{
1359 			if (CanDamage (ent, inflictor))
1360 			{
1361 				VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
1362 				T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
1363 			}
1364 		}
1365 	}
1366 }
1367 
1368 
1369 #define A2K_FRAMENUM	50
weapon_a2k_fire(edict_t * ent)1370 void weapon_a2k_fire (edict_t *ent)
1371 {
1372 	if (ent->client->ps.gunframe == 14)
1373 	{
1374 		ent->client->a2kFramenum = level.framenum + A2K_FRAMENUM;
1375 		ent->client->pers.inventory[ent->client->ammo_index]--;
1376 		ent->client->ps.gunframe++;
1377 
1378 		// start scream sound
1379 		//ent->client->weapon_sound = gi.soundindex("weapons/a2k/countdn.wav");
1380 		gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/a2k/countdn.wav"), 1, ATTN_NORM, 0);
1381 		//gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/a2k/ak_trig.wav"), 1, ATTN_NORM, 0);
1382 
1383 		// play quad sound
1384 		playQuadSound(ent);
1385 	}
1386 	else if (ent->client->a2kFramenum == level.framenum)
1387 	{
1388 		// boom
1389 		edict_t *exp = NULL;
1390 		float damage = 2500;
1391 		float dmg_radius = 512;
1392 		edict_t *e = NULL;
1393 		// play quad sound
1394 		playQuadSound(ent);
1395 		if (is_quad)
1396 		{
1397 			damage *= 4;
1398 			dmg_radius *= 4;
1399 		}
1400 		// do some damage
1401 		T_RadiusDamage(ent, ent, damage, NULL, dmg_radius, MOD_A2K);
1402 
1403 		// ok, now, do who ever's visible within 1024 units
1404 		Z_RadiusDamageVisible(ent, ent, damage, NULL, dmg_radius * 2, MOD_A2K);
1405 
1406 		exp = G_Spawn();
1407 		exp->classname = "A2K Explosion";
1408 		exp->solid = SOLID_NOT;
1409 		exp->movetype = MOVETYPE_NONE;
1410 		VectorClear(exp->mins);
1411 		VectorClear(exp->maxs);
1412 		VectorCopy(ent->s.origin, exp->s.origin);
1413 		exp->s.modelindex = gi.modelindex("models/objects/b_explode/tris.md2");
1414 		exp->s.frame = 0;
1415 		exp->s.skinnum = 6;
1416 		exp->think = weapon_a2k_exp_think;
1417 		exp->nextthink = level.time + FRAMETIME;
1418 		gi.linkentity(exp);
1419 		gi.positioned_sound(exp->s.origin, exp, CHAN_AUTO, gi.soundindex("weapons/a2k/ak_exp01.wav"), 1, ATTN_NORM, 0);
1420 		ent->client->ps.gunframe++;
1421 		ent->client->weapon_sound = 0;
1422 		return;
1423 	}
1424 	else if (ent->client->ps.gunframe == 19)
1425 	{
1426 		// don't increase the gunframe
1427 		return;
1428 	}
1429 }
1430 
Weapon_A2k(edict_t * ent)1431 void Weapon_A2k (edict_t *ent)
1432 {
1433 	/*
1434 		00-09 Active
1435 		10-19 Boom (14 actual fire frame)
1436 		20-29 Idle1
1437 		30-39 Idle2
1438 		40-49 Idle3
1439 		50-55 Away
1440 	*/
1441 	static int	pause_frames[]	= {20, 30, 40, 0};
1442 	static int	fire_frames[]	= {14, 19, 0};
1443 
1444 	Weapon_Generic (ent, 9, 19, 49, 55, pause_frames, fire_frames, weapon_a2k_fire);
1445 }
1446 
1447 /********************************************
1448 	Push
1449 
1450 	0 - Start push
1451 	4 - Contact
1452 	8 - End Push
1453 */
1454 
push_hit(edict_t * self,vec3_t start,vec3_t aim,int damage,int kick)1455 qboolean push_hit (edict_t *self, vec3_t start, vec3_t aim, int damage, int kick)
1456 {
1457 	trace_t tr;
1458 	vec3_t end;
1459 	vec3_t v;
1460 	edict_t *e = NULL;
1461 
1462 	//see if enemy is in range
1463 	VectorMA(start, 64, aim, end);
1464 	tr = gi.trace(start, NULL, NULL, end, self, MASK_SHOT);
1465 	if (tr.fraction >= 1)
1466 		return false;
1467 
1468 	// play sound
1469 	gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/push/contact.wav"), 1, ATTN_NORM, 0);
1470 
1471 	if (tr.ent->svflags & SVF_MONSTER ||
1472 		tr.ent->client)
1473 	{
1474 		// do our special form of knockback here
1475 		VectorMA (tr.ent->absmin, 0.75, tr.ent->size, v);
1476 		VectorSubtract (v, start, v);
1477 		VectorNormalize (v);
1478 		VectorMA (tr.ent->velocity, kick, v, tr.ent->velocity);
1479 		//tr.ent->velocity[2] = 48;
1480 		if (tr.ent->velocity[2] > 0)
1481 			tr.ent->groundentity = NULL;
1482 	}
1483 	else if (tr.ent->movetype == MOVETYPE_FALLFLOAT)
1484 	{
1485 		if (tr.ent->touch)
1486 		{
1487 			float mass = tr.ent->mass;
1488 			tr.ent->mass *= 0.25;
1489 			tr.ent->touch(tr.ent, self, NULL, NULL);
1490 			tr.ent->mass = mass;
1491 		}
1492 	}
1493 
1494 	// ok, we hit something, damage it
1495 	if (!tr.ent->takedamage)
1496 		return false;
1497 
1498 	// do the damage
1499 	T_Damage (tr.ent, self, self, aim, tr.endpos, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT);
1500 
1501 	return true;
1502 }
1503 
Action_Push(edict_t * ent)1504 void Action_Push (edict_t *ent)
1505 {
1506 	if (ent->client->ps.gunframe == 0)
1507 	{
1508 		// play grunt sound
1509 		//gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/push/grunt.wav"), 1, ATTN_NORM, 0);
1510 		ent->client->ps.gunframe++;
1511 	}
1512 	else if (ent->client->ps.gunframe == 4)
1513 	{
1514 		vec3_t forward;
1515 		vec3_t offset;
1516 		vec3_t start;
1517 
1518 		// contact
1519 		AngleVectors(ent->client->v_angle, forward, NULL, NULL);
1520 		VectorSet(offset, 0, 0, ent->viewheight * 0.5);
1521 		VectorAdd(ent->s.origin, offset, start);
1522 		push_hit(ent, start, forward, 2, 512);
1523 		ent->client->ps.gunframe++;
1524 	}
1525 	else if (ent->client->ps.gunframe == 8)
1526 	{
1527 		// go back to old weapon
1528 		ent->client->newweapon = ent->client->pers.lastweapon;
1529 		ChangeWeapon(ent);
1530 	}
1531 	else
1532 		ent->client->ps.gunframe++;
1533 }
1534 
1535