1 /*
2 * Copyright(c) 1997-2001 Id Software, Inc.
3 * Copyright(c) 2002 The Quakeforge Project.
4 * Copyright(c) 2006 Quetoo.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or(at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21 
22 #include "g_local.h"
23 #include "m_player.h"
24 
25 qboolean is_quad;
26 static byte is_silenced;
27 
28 void weapon_grenade_fire(edict_t *ent, qboolean held);
29 
P_ProjectSource(gclient_t * client,vec3_t point,vec3_t distance,vec3_t forward,vec3_t right,vec3_t result)30 void P_ProjectSource(gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result){
31 	vec3_t _distance;
32 
33 	VectorCopy(distance, _distance);
34 	if(client->pers.hand == LEFT_HANDED)
35 		_distance[1] *= -1;
36 	else if(client->pers.hand == CENTER_HANDED)
37 		_distance[1] = 0;
38 	G_ProjectSource(point, _distance, forward, right, result);
39 }
40 
41 
42 /*
43 PlayerNoise
44 
45 Each player can have two noise objects associated with it:
46 a personal noise(jumping, pain, weapon firing), and a weapon
47 target noise(bullet wall impacts)
48 
49 Monsters that don't directly see the player can move
50 to a noise in hopes of seeing the player from there.
51 */
PlayerNoise(edict_t * who,vec3_t where,int type)52 void PlayerNoise(edict_t *who, vec3_t where, int type){
53 	if(type == PNOISE_WEAPON){
54 		if(who->client->silencer_shots){
55 			who->client->silencer_shots--;
56 			return;
57 		}
58 	}
59 }
60 
61 
Pickup_Weapon(edict_t * ent,edict_t * other)62 qboolean Pickup_Weapon(edict_t *ent, edict_t *other){
63 	int index;
64 	gitem_t *ammo;
65 
66 	index = ITEM_INDEX(ent->item);
67 
68 	if(((int)(dmflags->value) & DF_WEAPONS_STAY) && other->client->pers.inventory[index]){
69 		if(!(ent->spawnflags &(DROPPED_ITEM | DROPPED_PLAYER_ITEM)))
70 			return false;  // leave the weapon for others to pickup
71 	}
72 
73 	other->client->pers.inventory[index]++;
74 
75 	if(!(ent->spawnflags & DROPPED_ITEM)){
76 		// give them some ammo with it
77 		ammo = FindItem(ent->item->ammo);
78 		if((int)dmflags->value & DF_INFINITE_AMMO)
79 			Add_Ammo(other, ammo, 1000);
80 		else
81 			Add_Ammo(other, ammo, ammo->quantity);
82 
83 		if(!(ent->spawnflags & DROPPED_PLAYER_ITEM)){
84 			if((int)(dmflags->value) & DF_WEAPONS_STAY)
85 				ent->flags |= FL_RESPAWN;
86 			else SetRespawn(ent, 30);
87 		}
88 	}
89 
90 	if(other->client->pers.weapon != ent->item &&
91 			(other->client->pers.inventory[index] == 1) &&
92 			(other->client->pers.weapon == FindItem("blaster")))
93 		other->client->newweapon = ent->item;
94 
95 	return true;
96 }
97 
98 
99 /*
100 ChangeWeapon
101 
102 The old weapon has been dropped all the way, so make the new one
103 current
104 */
ChangeWeapon(edict_t * ent)105 void ChangeWeapon(edict_t *ent){
106 	int i;
107 
108 	if(ent->client->grenade_time){
109 		ent->client->grenade_time = level.time;
110 		ent->client->weapon_sound = 0;
111 		weapon_grenade_fire(ent, false);
112 		ent->client->grenade_time = 0;
113 	}
114 
115 	ent->client->pers.lastweapon = ent->client->pers.weapon;
116 	ent->client->pers.weapon = ent->client->newweapon;
117 	ent->client->newweapon = NULL;
118 
119 	// set visible model
120 	if(ent->s.modelindex == 255){
121 		if(ent->client->pers.weapon)
122 			i =((ent->client->pers.weapon->weapmodel & 0xff) << 8);
123 		else
124 			i = 0;
125 		ent->s.skinnum =(ent - g_edicts - 1) | i;
126 	}
127 
128 	if(ent->client->pers.weapon && ent->client->pers.weapon->ammo)
129 		ent->client->ammo_index = ITEM_INDEX(FindItem(ent->client->pers.weapon->ammo));
130 	else
131 		ent->client->ammo_index = 0;
132 
133 	if(!ent->client->pers.weapon){  // dead
134 		ent->client->ps.gunindex = 0;
135 		return;
136 	}
137 
138 	ent->client->weaponstate = WEAPON_ACTIVATING;
139 	ent->client->ps.gunframe = 0;
140 	ent->client->ps.gunindex = gi.modelindex(ent->client->pers.weapon->view_model);
141 
142 	ent->client->anim_priority = ANIM_PAIN;
143 	if(ent->client->ps.pmove.pm_flags & PMF_DUCKED){
144 		ent->s.frame = FRAME_crpain1;
145 		ent->client->anim_end = FRAME_crpain4;
146 	} else {
147 		ent->s.frame = FRAME_pain301;
148 		ent->client->anim_end = FRAME_pain304;
149 
150 	}
151 }
152 
153 /*
154 NoAmmoWeaponChange
155 */
NoAmmoWeaponChange(edict_t * ent)156 void NoAmmoWeaponChange(edict_t *ent){
157 	if(ent->client->pers.inventory[ITEM_INDEX(FindItem("slugs"))]
158 			&& ent->client->pers.inventory[ITEM_INDEX(FindItem("railgun"))]){
159 		ent->client->newweapon = FindItem("railgun");
160 		return;
161 	}
162 	if(ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))]
163 			&& ent->client->pers.inventory[ITEM_INDEX(FindItem("hyperblaster"))]){
164 		ent->client->newweapon = FindItem("hyperblaster");
165 		return;
166 	}
167 	if (ent->client->pers.inventory[ITEM_INDEX(FindItem("rockets"))]
168 			&&  ent->client->pers.inventory[ITEM_INDEX(FindItem("rocket launcher"))]){
169 		ent->client->newweapon = FindItem ("rocket launcher");
170 		return;
171 	}
172 	if(ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]
173 			&& ent->client->pers.inventory[ITEM_INDEX(FindItem("chaingun"))]){
174 		ent->client->newweapon = FindItem("chaingun");
175 		return;
176 	}
177 	if(ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]
178 			&& ent->client->pers.inventory[ITEM_INDEX(FindItem("machinegun"))]){
179 		ent->client->newweapon = FindItem("machinegun");
180 		return;
181 	}
182 	if(ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))] > 1
183 			&& ent->client->pers.inventory[ITEM_INDEX(FindItem("super shotgun"))]){
184 		ent->client->newweapon = FindItem("super shotgun");
185 		return;
186 	}
187 	if(ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))]
188 			&& ent->client->pers.inventory[ITEM_INDEX(FindItem("shotgun"))]){
189 		ent->client->newweapon = FindItem("shotgun");
190 		return;
191 	}
192 	ent->client->newweapon = FindItem("blaster");
193 }
194 
195 /*
196 Think_Weapon
197 
198 Called by ClientBeginServerFrame and ClientThink
199 */
Think_Weapon(edict_t * ent)200 void Think_Weapon(edict_t *ent){
201 	// if just died, put the weapon away
202 	if(ent->health < 1){
203 		ent->client->newweapon = NULL;
204 		ChangeWeapon(ent);
205 	}
206 
207 	// call active weapon think routine
208 	if(ent->client->pers.weapon && ent->client->pers.weapon->weaponthink){
209 		is_quad =(ent->client->quad_framenum > level.framenum);
210 		if(ent->client->silencer_shots)
211 			is_silenced = MZ_SILENCED;
212 		else
213 			is_silenced = 0;
214 		ent->client->pers.weapon->weaponthink(ent);
215 	}
216 }
217 
218 
219 /*
220 Use_Weapon
221 
222 Make the weapon ready if there is ammo
223 */
Use_Weapon(edict_t * ent,gitem_t * item)224 void Use_Weapon(edict_t *ent, gitem_t *item){
225 	int ammo_index;
226 	gitem_t *ammo_item;
227 
228 	// see if we're already using it
229 	if(item == ent->client->pers.weapon)
230 		return;
231 
232 	if(item->ammo && !g_select_empty->value && !(item->flags & IT_AMMO)){
233 		ammo_item = FindItem(item->ammo);
234 		ammo_index = ITEM_INDEX(ammo_item);
235 
236 		if(!ent->client->pers.inventory[ammo_index]){
237 			gi.cprintf(ent, PRINT_HIGH, "No %s for %s.\n", ammo_item->pickup_name, item->pickup_name);
238 			return;
239 		}
240 
241 		if(ent->client->pers.inventory[ammo_index] < item->quantity){
242 			gi.cprintf(ent, PRINT_HIGH, "Not enough %s for %s.\n", ammo_item->pickup_name, item->pickup_name);
243 			return;
244 		}
245 	}
246 
247 	// change to this weapon when down
248 	ent->client->newweapon = item;
249 }
250 
251 
252 
253 /*
254 Drop_Weapon
255 */
Drop_Weapon(edict_t * ent,gitem_t * item)256 void Drop_Weapon(edict_t *ent, gitem_t *item){
257 	int index;
258 
259 	if((int)(dmflags->value) & DF_WEAPONS_STAY)
260 		return;
261 
262 	index = ITEM_INDEX(item);
263 	// see if we're already using it
264 	if(((item == ent->client->pers.weapon) ||(item == ent->client->newweapon)) &&(ent->client->pers.inventory[index] == 1)){
265 		gi.cprintf(ent, PRINT_HIGH, "Can't drop current weapon\n");
266 		return;
267 	}
268 
269 	Drop_Item(ent, item);
270 	ent->client->pers.inventory[index]--;
271 }
272 
273 
274 /*
275 Weapon_Generic
276 
277 A generic function to handle the basics of weapon thinking
278 */
279 #define FRAME_FIRE_FIRST		(FRAME_ACTIVATE_LAST + 1)
280 #define FRAME_IDLE_FIRST		(FRAME_FIRE_LAST + 1)
281 #define FRAME_DEACTIVATE_FIRST	(FRAME_IDLE_LAST + 1)
282 
Weapon_Generic(edict_t * ent,int FRAME_ACTIVATE_LAST,int FRAME_FIRE_LAST,int FRAME_IDLE_LAST,int FRAME_DEACTIVATE_LAST,int * pause_frames,int * fire_frames,void (* fire)(edict_t * ent))283 void Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames, int *fire_frames, void(*fire)(edict_t *ent)){
284 	int n;
285 
286 	if(ent->deadflag || ent->s.modelindex != 255) // VWep animations screw up corpses
287 	{
288 		return;
289 	}
290 
291 	if(ent->client->weaponstate == WEAPON_DROPPING){
292 		if(ent->client->ps.gunframe == FRAME_DEACTIVATE_LAST){
293 			ChangeWeapon(ent);
294 			return;
295 		} else if((FRAME_DEACTIVATE_LAST - ent->client->ps.gunframe) == 4){
296 			ent->client->anim_priority = ANIM_REVERSE;
297 			if(ent->client->ps.pmove.pm_flags & PMF_DUCKED){
298 				ent->s.frame = FRAME_crpain4 + 1;
299 				ent->client->anim_end = FRAME_crpain1;
300 			} else {
301 				ent->s.frame = FRAME_pain304 + 1;
302 				ent->client->anim_end = FRAME_pain301;
303 
304 			}
305 		}
306 
307 		ent->client->ps.gunframe++;
308 		return;
309 	}
310 
311 	if(ent->client->weaponstate == WEAPON_ACTIVATING){
312 		if(ent->client->ps.gunframe == FRAME_ACTIVATE_LAST){
313 			ent->client->weaponstate = WEAPON_READY;
314 			ent->client->ps.gunframe = FRAME_IDLE_FIRST;
315 			return;
316 		}
317 
318 		ent->client->ps.gunframe++;
319 		return;
320 	}
321 
322 	if((ent->client->newweapon) &&(ent->client->weaponstate != WEAPON_FIRING)){
323 		ent->client->weaponstate = WEAPON_DROPPING;
324 		ent->client->ps.gunframe = FRAME_DEACTIVATE_FIRST;
325 
326 		if((FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) < 4){
327 			ent->client->anim_priority = ANIM_REVERSE;
328 			if(ent->client->ps.pmove.pm_flags & PMF_DUCKED){
329 				ent->s.frame = FRAME_crpain4 + 1;
330 				ent->client->anim_end = FRAME_crpain1;
331 			} else {
332 				ent->s.frame = FRAME_pain304 + 1;
333 				ent->client->anim_end = FRAME_pain301;
334 
335 			}
336 		}
337 		return;
338 	}
339 
340 	if(ent->client->weaponstate == WEAPON_READY){
341 		if(((ent->client->latched_buttons | ent->client->buttons) & BUTTON_ATTACK)){
342 			ent->client->latched_buttons &= ~BUTTON_ATTACK;
343 			if((!ent->client->ammo_index) ||
344 					(ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity)){
345 				ent->client->ps.gunframe = FRAME_FIRE_FIRST;
346 				ent->client->weaponstate = WEAPON_FIRING;
347 
348 	// start the animation
349 				ent->client->anim_priority = ANIM_ATTACK;
350 				if(ent->client->ps.pmove.pm_flags & PMF_DUCKED){
351 					ent->s.frame = FRAME_crattak1 - 1;
352 					ent->client->anim_end = FRAME_crattak9;
353 				} else {
354 					ent->s.frame = FRAME_attack1 - 1;
355 					ent->client->anim_end = FRAME_attack8;
356 				}
357 			} else {
358 				if(level.time >= ent->pain_debounce_time){
359 					gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
360 					ent->pain_debounce_time = level.time + 1;
361 				}
362 				NoAmmoWeaponChange(ent);
363 			}
364 		} else {
365 			if(ent->client->ps.gunframe == FRAME_IDLE_LAST){
366 				ent->client->ps.gunframe = FRAME_IDLE_FIRST;
367 				return;
368 			}
369 
370 			if(pause_frames){
371 				for(n = 0; pause_frames[n]; n++){
372 					if(ent->client->ps.gunframe == pause_frames[n]){
373 						if(rand()&15)
374 							return;
375 					}
376 				}
377 			}
378 
379 			ent->client->ps.gunframe++;
380 			return;
381 		}
382 	}
383 
384 	if(ent->client->weaponstate == WEAPON_FIRING){
385 		for(n = 0; fire_frames[n]; n++){
386 			if(ent->client->ps.gunframe == fire_frames[n]){
387 				if(ent->client->quad_framenum > level.framenum)
388 					gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
389 
390 				fire(ent);
391 				break;
392 			}
393 		}
394 
395 		if(!fire_frames[n])
396 			ent->client->ps.gunframe++;
397 
398 		if(ent->client->ps.gunframe == FRAME_IDLE_FIRST + 1)
399 			ent->client->weaponstate = WEAPON_READY;
400 	}
401 }
402 
403 
404 /*
405 
406 GRENADE
407 
408 */
409 
410 #define GRENADE_TIMER		3.0
411 #define GRENADE_MINSPEED	400
412 #define GRENADE_MAXSPEED	800
413 
weapon_grenade_fire(edict_t * ent,qboolean held)414 void weapon_grenade_fire(edict_t *ent, qboolean held){
415 	vec3_t offset;
416 	vec3_t forward, right;
417 	vec3_t start;
418 	int damage = 125;
419 	float timer;
420 	int speed;
421 	float radius;
422 
423 	radius = damage + 40;
424 	if(is_quad)
425 		damage *= 4;
426 
427 	VectorSet(offset, 8, 8, ent->viewheight - 8);
428 	AngleVectors(ent->client->v_angle, forward, right, NULL);
429 	P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
430 
431 	timer = ent->client->grenade_time - level.time;
432 	speed = GRENADE_MINSPEED +(GRENADE_TIMER - timer) *((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER);
433 	fire_grenade2(ent, start, forward, damage, speed, timer, radius, held);
434 
435 	if(!((int)dmflags->value & DF_INFINITE_AMMO))
436 		ent->client->pers.inventory[ent->client->ammo_index]--;
437 
438 	ent->client->grenade_time = level.time + 1.0;
439 
440 	if(ent->deadflag || ent->s.modelindex != 255) // VWep animations screw up corpses
441 	{
442 		return;
443 	}
444 
445 	if(ent->health <= 0)
446 		return;
447 
448 	if(ent->client->ps.pmove.pm_flags & PMF_DUCKED){
449 		ent->client->anim_priority = ANIM_ATTACK;
450 		ent->s.frame = FRAME_crattak1 - 1;
451 		ent->client->anim_end = FRAME_crattak3;
452 	} else {
453 		ent->client->anim_priority = ANIM_REVERSE;
454 		ent->s.frame = FRAME_wave08;
455 		ent->client->anim_end = FRAME_wave01;
456 	}
457 }
458 
Weapon_Grenade(edict_t * ent)459 void Weapon_Grenade(edict_t *ent){
460 	if((ent->client->newweapon) &&(ent->client->weaponstate == WEAPON_READY)){
461 		ChangeWeapon(ent);
462 		return;
463 	}
464 
465 	if(ent->client->weaponstate == WEAPON_ACTIVATING){
466 		ent->client->weaponstate = WEAPON_READY;
467 		ent->client->ps.gunframe = 16;
468 		return;
469 	}
470 
471 	if(ent->client->weaponstate == WEAPON_READY){
472 		if(((ent->client->latched_buttons | ent->client->buttons) & BUTTON_ATTACK)){
473 			ent->client->latched_buttons &= ~BUTTON_ATTACK;
474 			if(ent->client->pers.inventory[ent->client->ammo_index]){
475 				ent->client->ps.gunframe = 1;
476 				ent->client->weaponstate = WEAPON_FIRING;
477 				ent->client->grenade_time = 0;
478 			} else {
479 				if(level.time >= ent->pain_debounce_time){
480 					gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
481 					ent->pain_debounce_time = level.time + 1;
482 				}
483 				NoAmmoWeaponChange(ent);
484 			}
485 			return;
486 		}
487 
488 		if((ent->client->ps.gunframe == 29) ||(ent->client->ps.gunframe == 34) ||(ent->client->ps.gunframe == 39) ||(ent->client->ps.gunframe == 48)){
489 			if(rand()&15)
490 				return;
491 		}
492 
493 		if(++ent->client->ps.gunframe > 48)
494 			ent->client->ps.gunframe = 16;
495 		return;
496 	}
497 
498 	if(ent->client->weaponstate == WEAPON_FIRING){
499 		if(ent->client->ps.gunframe == 5)
500 			gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/hgrena1b.wav"), 1, ATTN_NORM, 0);
501 
502 		if(ent->client->ps.gunframe == 11){
503 			if(!ent->client->grenade_time){
504 				ent->client->grenade_time = level.time + GRENADE_TIMER + 0.2;
505 				ent->client->weapon_sound = gi.soundindex("weapons/hgrenc1b.wav");
506 			}
507 
508 	// they waited too long, detonate it in their hand
509 			if(!ent->client->grenade_blew_up && level.time >= ent->client->grenade_time){
510 				ent->client->weapon_sound = 0;
511 				weapon_grenade_fire(ent, true);
512 				ent->client->grenade_blew_up = true;
513 			}
514 
515 			if(ent->client->buttons & BUTTON_ATTACK)
516 				return;
517 
518 			if(ent->client->grenade_blew_up){
519 				if(level.time >= ent->client->grenade_time){
520 					ent->client->ps.gunframe = 15;
521 					ent->client->grenade_blew_up = false;
522 				} else {
523 					return;
524 				}
525 			}
526 		}
527 
528 		if(ent->client->ps.gunframe == 12){
529 			ent->client->weapon_sound = 0;
530 			weapon_grenade_fire(ent, false);
531 		}
532 
533 		if((ent->client->ps.gunframe == 15) &&(level.time < ent->client->grenade_time))
534 			return;
535 
536 		ent->client->ps.gunframe++;
537 
538 		if(ent->client->ps.gunframe == 16){
539 			ent->client->grenade_time = 0;
540 			ent->client->weaponstate = WEAPON_READY;
541 		}
542 	}
543 }
544 
545 /*
546 
547 GRENADE LAUNCHER
548 
549 */
550 
weapon_grenadelauncher_fire(edict_t * ent)551 void weapon_grenadelauncher_fire(edict_t *ent){
552 	vec3_t offset;
553 	vec3_t forward, right;
554 	vec3_t start;
555 	int damage = 120;
556 	float radius;
557 
558 	radius = damage + 40;
559 	if(is_quad)
560 		damage *= 4;
561 
562 	VectorSet(offset, 8, 8, ent->viewheight - 8);
563 	AngleVectors(ent->client->v_angle, forward, right, NULL);
564 	P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
565 
566 	VectorScale(forward, -2, ent->client->kick_origin);
567 	ent->client->kick_angles[0] = -1;
568 
569 	fire_grenade(ent, start, forward, damage, 600, 2.5, radius);
570 
571 	gi.WriteByte(svc_muzzleflash);
572 	gi.WriteShort(ent - g_edicts);
573 	gi.WriteByte(MZ_GRENADE | is_silenced);
574 	gi.multicast(ent->s.origin, MULTICAST_PVS);
575 
576 	ent->client->ps.gunframe++;
577 
578 	PlayerNoise(ent, start, PNOISE_WEAPON);
579 
580 	if(!((int)dmflags->value & DF_INFINITE_AMMO))
581 		ent->client->pers.inventory[ent->client->ammo_index]--;
582 }
583 
Weapon_GrenadeLauncher(edict_t * ent)584 void Weapon_GrenadeLauncher(edict_t *ent){
585 	static int pause_frames[]	= {34, 51, 59, 0};
586 	static int fire_frames[]	= {6, 0};
587 
588 	Weapon_Generic(ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_grenadelauncher_fire);
589 }
590 
591 /*
592 
593 ROCKET
594 
595 */
596 
Weapon_RocketLauncher_Fire(edict_t * ent)597 void Weapon_RocketLauncher_Fire(edict_t *ent){
598 	vec3_t offset, start;
599 	vec3_t forward, right;
600 	int damage;
601 	float damage_radius;
602 	int radius_damage;
603 
604 	damage = 100 +(int)(random() * 20.0);
605 	radius_damage = 120;
606 	damage_radius = 120;
607 	if(is_quad){
608 		damage *= 4;
609 		radius_damage *= 4;
610 	}
611 
612 	AngleVectors(ent->client->v_angle, forward, right, NULL);
613 
614 	VectorScale(forward, -2, ent->client->kick_origin);
615 	ent->client->kick_angles[0] = -1;
616 
617 	VectorSet(offset, 8, 8, ent->viewheight - 8);
618 	P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
619 	fire_rocket(ent, start, forward, damage, 650, damage_radius, radius_damage);
620 
621 	// send muzzle flash
622 	gi.WriteByte(svc_muzzleflash);
623 	gi.WriteShort(ent - g_edicts);
624 	gi.WriteByte(MZ_ROCKET | is_silenced);
625 	gi.multicast(ent->s.origin, MULTICAST_PVS);
626 
627 	ent->client->ps.gunframe++;
628 
629 	PlayerNoise(ent, start, PNOISE_WEAPON);
630 
631 	if(!((int)dmflags->value & DF_INFINITE_AMMO))
632 		ent->client->pers.inventory[ent->client->ammo_index]--;
633 }
634 
Weapon_RocketLauncher(edict_t * ent)635 void Weapon_RocketLauncher(edict_t *ent){
636 	static int pause_frames[]	= {25, 33, 42, 50, 0};
637 	static int fire_frames[]	= {5, 0};
638 
639 	Weapon_Generic(ent, 4, 12, 50, 54, pause_frames, fire_frames, Weapon_RocketLauncher_Fire);
640 }
641 
642 
643 /*
644 
645 BLASTER / HYPERBLASTER
646 
647 */
648 
Blaster_Fire(edict_t * ent,vec3_t g_offset,int damage,qboolean hyper,int effect)649 void Blaster_Fire(edict_t *ent, vec3_t g_offset, int damage, qboolean hyper, int effect){
650 	vec3_t forward, right;
651 	vec3_t start;
652 	vec3_t offset;
653 
654 	if(is_quad)
655 		damage *= 4;
656 	AngleVectors(ent->client->v_angle, forward, right, NULL);
657 	VectorSet(offset, 24, 8, ent->viewheight - 8);
658 	VectorAdd(offset, g_offset, offset);
659 	P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
660 
661 	VectorScale(forward, -2, ent->client->kick_origin);
662 	ent->client->kick_angles[0] = -1;
663 
664 	fire_blaster(ent, start, forward, damage, 1000, effect, hyper);
665 
666 	// send muzzle flash
667 	gi.WriteByte(svc_muzzleflash);
668 	gi.WriteShort(ent - g_edicts);
669 	if(hyper)
670 		gi.WriteByte(MZ_HYPERBLASTER | is_silenced);
671 	else
672 		gi.WriteByte(MZ_BLASTER | is_silenced);
673 	gi.multicast(ent->s.origin, MULTICAST_PVS);
674 
675 	PlayerNoise(ent, start, PNOISE_WEAPON);
676 }
677 
678 
Weapon_Blaster_Fire(edict_t * ent)679 void Weapon_Blaster_Fire(edict_t *ent){
680 	Blaster_Fire(ent, vec3_origin, 15, false, EF_BLASTER);
681 	ent->client->ps.gunframe++;
682 }
683 
Weapon_Blaster(edict_t * ent)684 void Weapon_Blaster(edict_t *ent){
685 	static int pause_frames[]	= {19, 32, 0};
686 	static int fire_frames[]	= {5, 0};
687 
688 	Weapon_Generic(ent, 4, 8, 52, 55, pause_frames, fire_frames, Weapon_Blaster_Fire);
689 }
690 
691 
Weapon_HyperBlaster_Fire(edict_t * ent)692 void Weapon_HyperBlaster_Fire(edict_t *ent){
693 	float rotation;
694 	vec3_t offset;
695 	int effect;
696 
697 	ent->client->weapon_sound = gi.soundindex("weapons/hyprbl1a.wav");
698 
699 	if(!(ent->client->buttons & BUTTON_ATTACK)){
700 		ent->client->ps.gunframe++;
701 	} else {
702 		if(! ent->client->pers.inventory[ent->client->ammo_index]){
703 			if(level.time >= ent->pain_debounce_time){
704 				gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
705 				ent->pain_debounce_time = level.time + 1;
706 			}
707 			NoAmmoWeaponChange(ent);
708 		} else {
709 			rotation =(ent->client->ps.gunframe - 5) * 2 * M_PI / 6;
710 			offset[0] = -4 * sin(rotation);
711 			offset[1] = 0;
712 			offset[2] = 4 * cos(rotation);
713 
714 			if((ent->client->ps.gunframe == 6) ||(ent->client->ps.gunframe == 9))
715 				effect = EF_HYPERBLASTER;
716 			else effect = 0;
717 
718 			Blaster_Fire(ent, offset, 15, true, effect);
719 
720 			if(!((int)dmflags->value & DF_INFINITE_AMMO))
721 				ent->client->pers.inventory[ent->client->ammo_index]--;
722 
723 			ent->client->anim_priority = ANIM_ATTACK;
724 			if(ent->client->ps.pmove.pm_flags & PMF_DUCKED){
725 				ent->s.frame = FRAME_crattak1 - 1;
726 				ent->client->anim_end = FRAME_crattak9;
727 			} else {
728 				ent->s.frame = FRAME_attack1 - 1;
729 				ent->client->anim_end = FRAME_attack8;
730 			}
731 		}
732 
733 		ent->client->ps.gunframe++;
734 		if(ent->client->ps.gunframe == 12 && ent->client->pers.inventory[ent->client->ammo_index])
735 			ent->client->ps.gunframe = 6;
736 	}
737 
738 	if(ent->client->ps.gunframe == 12){
739 		gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/hyprbd1a.wav"), 1, ATTN_NORM, 0);
740 		ent->client->weapon_sound = 0;
741 	}
742 
743 }
744 
Weapon_HyperBlaster(edict_t * ent)745 void Weapon_HyperBlaster(edict_t *ent){
746 	static int pause_frames[]	= {0};
747 	static int fire_frames[]	= {6, 7, 8, 9, 10, 11, 0};
748 
749 	Weapon_Generic(ent, 5, 20, 49, 53, pause_frames, fire_frames, Weapon_HyperBlaster_Fire);
750 }
751 
752 /*
753 
754 MACHINEGUN / CHAINGUN
755 
756 */
757 
Machinegun_Fire(edict_t * ent)758 void Machinegun_Fire(edict_t *ent){
759 	int i;
760 	vec3_t start;
761 	vec3_t forward, right;
762 	vec3_t angles;
763 	int damage = 8;
764 	int kick = 2;
765 	vec3_t offset;
766 
767 	if(!(ent->client->buttons & BUTTON_ATTACK)){
768 		ent->client->ps.gunframe++;
769 		return;
770 	}
771 
772 	if(ent->client->ps.gunframe == 5)
773 		ent->client->ps.gunframe = 4;
774 	else
775 		ent->client->ps.gunframe = 5;
776 
777 	if(ent->client->pers.inventory[ent->client->ammo_index] < 1){
778 		ent->client->ps.gunframe = 6;
779 		if(level.time >= ent->pain_debounce_time){
780 			gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
781 			ent->pain_debounce_time = level.time + 1;
782 		}
783 		NoAmmoWeaponChange(ent);
784 		return;
785 	}
786 
787 	if(is_quad){
788 		damage *= 4;
789 		kick *= 4;
790 	}
791 
792 	for(i = 1; i < 3; i++){
793 		ent->client->kick_origin[i] = crandom() * 0.35;
794 		ent->client->kick_angles[i] = crandom() * 0.7;
795 	}
796 
797 	// get start / end positions
798 	VectorAdd(ent->client->v_angle, ent->client->kick_angles, angles);
799 	AngleVectors(angles, forward, right, NULL);
800 	VectorSet(offset, 0, 8, ent->viewheight - 8);
801 	P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
802 	fire_bullet(ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN);
803 
804 	gi.WriteByte(svc_muzzleflash);
805 	gi.WriteShort(ent - g_edicts);
806 	gi.WriteByte(MZ_MACHINEGUN | is_silenced);
807 	gi.multicast(ent->s.origin, MULTICAST_PVS);
808 
809 	PlayerNoise(ent, start, PNOISE_WEAPON);
810 
811 	if(!((int)dmflags->value & DF_INFINITE_AMMO))
812 		ent->client->pers.inventory[ent->client->ammo_index]--;
813 
814 	ent->client->anim_priority = ANIM_ATTACK;
815 	if(ent->client->ps.pmove.pm_flags & PMF_DUCKED){
816 		ent->s.frame = FRAME_crattak1 -(int)(random() + 0.25);
817 		ent->client->anim_end = FRAME_crattak9;
818 	} else {
819 		ent->s.frame = FRAME_attack1 -(int)(random() + 0.25);
820 		ent->client->anim_end = FRAME_attack8;
821 	}
822 }
823 
Weapon_Machinegun(edict_t * ent)824 void Weapon_Machinegun(edict_t *ent){
825 	static int pause_frames[] = {23, 45, 0};
826 	static int fire_frames[] = {4, 5, 0};
827 
828 	Weapon_Generic(ent, 3, 5, 45, 49, pause_frames, fire_frames, Machinegun_Fire);
829 }
830 
Chaingun_Fire(edict_t * ent)831 void Chaingun_Fire(edict_t *ent){
832 	int i;
833 	int shots;
834 	vec3_t start;
835 	vec3_t forward, right, up;
836 	float r, u;
837 	vec3_t offset;
838 	int damage = 6;
839 	int kick = 2;
840 
841 	if(ent->client->ps.gunframe == 5)
842 		gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnu1a.wav"), 1, ATTN_IDLE, 0);
843 
844 	if((ent->client->ps.gunframe == 14) && !(ent->client->buttons & BUTTON_ATTACK)){
845 		ent->client->ps.gunframe = 32;
846 		ent->client->weapon_sound = 0;
847 		return;
848 	} else if((ent->client->ps.gunframe == 21) &&(ent->client->buttons & BUTTON_ATTACK)
849 			   && ent->client->pers.inventory[ent->client->ammo_index]){
850 		ent->client->ps.gunframe = 15;
851 	} else {
852 		ent->client->ps.gunframe++;
853 	}
854 
855 	if(ent->client->ps.gunframe == 22){
856 		ent->client->weapon_sound = 0;
857 		gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnd1a.wav"), 1, ATTN_IDLE, 0);
858 	} else {
859 		ent->client->weapon_sound = gi.soundindex("weapons/chngnl1a.wav");
860 	}
861 
862 	ent->client->anim_priority = ANIM_ATTACK;
863 	if(ent->client->ps.pmove.pm_flags & PMF_DUCKED){
864 		ent->s.frame = FRAME_crattak1 -(ent->client->ps.gunframe & 1);
865 		ent->client->anim_end = FRAME_crattak9;
866 	} else {
867 		ent->s.frame = FRAME_attack1 -(ent->client->ps.gunframe & 1);
868 		ent->client->anim_end = FRAME_attack8;
869 	}
870 
871 	if(ent->client->ps.gunframe <= 9)
872 		shots = 1;
873 	else if(ent->client->ps.gunframe <= 14){
874 		if(ent->client->buttons & BUTTON_ATTACK)
875 			shots = 2;
876 		else
877 			shots = 1;
878 	} else
879 		shots = 3;
880 
881 	if(ent->client->pers.inventory[ent->client->ammo_index] < shots)
882 		shots = ent->client->pers.inventory[ent->client->ammo_index];
883 
884 	if(!shots){
885 		if(level.time >= ent->pain_debounce_time){
886 			gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
887 			ent->pain_debounce_time = level.time + 1;
888 		}
889 		NoAmmoWeaponChange(ent);
890 		return;
891 	}
892 
893 	if(is_quad){
894 		damage *= 4;
895 		kick *= 4;
896 	}
897 
898 	for(i = 0; i < 3; i++){
899 		ent->client->kick_origin[i] = crandom() * 0.35;
900 		ent->client->kick_angles[i] = crandom() * 0.7;
901 	}
902 
903 	for(i = 0; i < shots; i++){
904 	// get start / end positions
905 		AngleVectors(ent->client->v_angle, forward, right, up);
906 		r = 7 + crandom() * 4;
907 		u = crandom() * 4;
908 		VectorSet(offset, 0, r, u + ent->viewheight - 8);
909 		P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
910 
911 		fire_bullet(ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_CHAINGUN);
912 	}
913 
914 	// send muzzle flash
915 	gi.WriteByte(svc_muzzleflash);
916 	gi.WriteShort(ent - g_edicts);
917 	gi.WriteByte((MZ_CHAINGUN1 + shots - 1) | is_silenced);
918 	gi.multicast(ent->s.origin, MULTICAST_PVS);
919 
920 	PlayerNoise(ent, start, PNOISE_WEAPON);
921 
922 	if(!((int)dmflags->value & DF_INFINITE_AMMO))
923 		ent->client->pers.inventory[ent->client->ammo_index] -= shots;
924 }
925 
926 
Weapon_Chaingun(edict_t * ent)927 void Weapon_Chaingun(edict_t *ent){
928 	static int pause_frames[]	= {38, 43, 51, 61, 0};
929 	static int fire_frames[]	= {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0};
930 
931 	Weapon_Generic(ent, 4, 31, 61, 64, pause_frames, fire_frames, Chaingun_Fire);
932 }
933 
934 
935 /*
936 
937 SHOTGUN / SUPERSHOTGUN
938 
939 */
940 
weapon_shotgun_fire(edict_t * ent)941 void weapon_shotgun_fire(edict_t *ent){
942 	vec3_t start;
943 	vec3_t forward, right;
944 	vec3_t offset;
945 	int damage = 4;
946 	int kick = 8;
947 
948 	if(ent->client->ps.gunframe == 9){
949 		ent->client->ps.gunframe++;
950 		return;
951 	}
952 
953 	AngleVectors(ent->client->v_angle, forward, right, NULL);
954 
955 	VectorScale(forward, -2, ent->client->kick_origin);
956 	ent->client->kick_angles[0] = -2;
957 
958 	VectorSet(offset, 0, 8, ent->viewheight - 8);
959 	P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
960 
961 	if(is_quad){
962 		damage *= 4;
963 		kick *= 4;
964 	}
965 
966 	fire_shotgun(ent, start, forward, damage, kick, 500, 500, DEFAULT_DEATHMATCH_SHOTGUN_COUNT, MOD_SHOTGUN);
967 
968 	// send muzzle flash
969 	gi.WriteByte(svc_muzzleflash);
970 	gi.WriteShort(ent - g_edicts);
971 	gi.WriteByte(MZ_SHOTGUN | is_silenced);
972 	gi.multicast(ent->s.origin, MULTICAST_PVS);
973 
974 	ent->client->ps.gunframe++;
975 	PlayerNoise(ent, start, PNOISE_WEAPON);
976 
977 	if(!((int)dmflags->value & DF_INFINITE_AMMO))
978 		ent->client->pers.inventory[ent->client->ammo_index]--;
979 }
980 
Weapon_Shotgun(edict_t * ent)981 void Weapon_Shotgun(edict_t *ent){
982 	static int pause_frames[]	= {22, 28, 34, 0};
983 	static int fire_frames[]	= {8, 9, 0};
984 
985 	Weapon_Generic(ent, 7, 18, 36, 39, pause_frames, fire_frames, weapon_shotgun_fire);
986 }
987 
988 
weapon_supershotgun_fire(edict_t * ent)989 void weapon_supershotgun_fire(edict_t *ent){
990 	vec3_t start;
991 	vec3_t forward, right;
992 	vec3_t offset;
993 	vec3_t v;
994 	int damage = 6;
995 	int kick = 12;
996 
997 	AngleVectors(ent->client->v_angle, forward, right, NULL);
998 
999 	VectorScale(forward, -2, ent->client->kick_origin);
1000 	ent->client->kick_angles[0] = -2;
1001 
1002 	VectorSet(offset, 0, 8, ent->viewheight - 8);
1003 	P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
1004 
1005 	if(is_quad){
1006 		damage *= 4;
1007 		kick *= 4;
1008 	}
1009 
1010 	v[PITCH] = ent->client->v_angle[PITCH];
1011 	v[YAW] = ent->client->v_angle[YAW] - 5;
1012 	v[ROLL] = ent->client->v_angle[ROLL];
1013 	AngleVectors(v, forward, NULL, NULL);
1014 	fire_shotgun(ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT / 2, MOD_SSHOTGUN);
1015 	v[YAW] = ent->client->v_angle[YAW] + 5;
1016 	AngleVectors(v, forward, NULL, NULL);
1017 	fire_shotgun(ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT / 2, MOD_SSHOTGUN);
1018 
1019 	// send muzzle flash
1020 	gi.WriteByte(svc_muzzleflash);
1021 	gi.WriteShort(ent - g_edicts);
1022 	gi.WriteByte(MZ_SSHOTGUN | is_silenced);
1023 	gi.multicast(ent->s.origin, MULTICAST_PVS);
1024 
1025 	ent->client->ps.gunframe++;
1026 	PlayerNoise(ent, start, PNOISE_WEAPON);
1027 
1028 	if(!((int)dmflags->value & DF_INFINITE_AMMO))
1029 		ent->client->pers.inventory[ent->client->ammo_index] -= 2;
1030 }
1031 
Weapon_SuperShotgun(edict_t * ent)1032 void Weapon_SuperShotgun(edict_t *ent){
1033 	static int pause_frames[]	= {29, 42, 57, 0};
1034 	static int fire_frames[]	= {7, 0};
1035 
1036 	Weapon_Generic(ent, 6, 17, 57, 61, pause_frames, fire_frames, weapon_supershotgun_fire);
1037 }
1038 
1039 
1040 
1041 /*
1042 
1043 RAILGUN
1044 
1045 */
1046 
weapon_railgun_fire(edict_t * ent)1047 void weapon_railgun_fire(edict_t *ent){
1048 	vec3_t start;
1049 	vec3_t forward, right;
1050 	vec3_t offset;
1051 	int damage = 100;
1052 	int kick = 200;
1053 
1054 	if(is_quad){
1055 		damage *= 4;
1056 		kick *= 4;
1057 	}
1058 
1059 	AngleVectors(ent->client->v_angle, forward, right, NULL);
1060 
1061 	VectorScale(forward, -3, ent->client->kick_origin);
1062 	ent->client->kick_angles[0] = -3;
1063 
1064 	VectorSet(offset, 0, 7, ent->viewheight - 8);
1065 	P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
1066 	fire_rail(ent, start, forward, damage, kick);
1067 
1068 	// send muzzle flash
1069 	gi.WriteByte(svc_muzzleflash);
1070 	gi.WriteShort(ent - g_edicts);
1071 	gi.WriteByte(MZ_RAILGUN | is_silenced);
1072 	gi.multicast(ent->s.origin, MULTICAST_PVS);
1073 
1074 	ent->client->ps.gunframe++;
1075 	PlayerNoise(ent, start, PNOISE_WEAPON);
1076 
1077 	if(!((int)dmflags->value & DF_INFINITE_AMMO))
1078 		ent->client->pers.inventory[ent->client->ammo_index]--;
1079 }
1080 
1081 
Weapon_Railgun(edict_t * ent)1082 void Weapon_Railgun(edict_t *ent){
1083 	static int pause_frames[]	= {56, 0};
1084 	static int fire_frames[]	= {4, 0};
1085 
1086 	Weapon_Generic(ent, 3, 18, 56, 61, pause_frames, fire_frames, weapon_railgun_fire);
1087 }
1088 
1089 
1090 /*
1091 
1092 BFG10K
1093 
1094 */
1095 
weapon_bfg_fire(edict_t * ent)1096 void weapon_bfg_fire(edict_t *ent){
1097 	vec3_t offset, start;
1098 	vec3_t forward, right;
1099 	int damage = 200;
1100 	float damage_radius = 1000;
1101 
1102 	if(ent->client->ps.gunframe == 9){
1103 	// send muzzle flash
1104 		gi.WriteByte(svc_muzzleflash);
1105 		gi.WriteShort(ent - g_edicts);
1106 		gi.WriteByte(MZ_BFG | is_silenced);
1107 		gi.multicast(ent->s.origin, MULTICAST_PVS);
1108 
1109 		ent->client->ps.gunframe++;
1110 
1111 		PlayerNoise(ent, start, PNOISE_WEAPON);
1112 		return;
1113 	}
1114 
1115 	// cells can go down during windup(from power armor hits), so
1116 	// check again and abort firing if we don't have enough now
1117 	if(ent->client->pers.inventory[ent->client->ammo_index] < 50){
1118 		ent->client->ps.gunframe++;
1119 		return;
1120 	}
1121 
1122 	if(is_quad)
1123 		damage *= 4;
1124 
1125 	AngleVectors(ent->client->v_angle, forward, right, NULL);
1126 
1127 	VectorScale(forward, -2, ent->client->kick_origin);
1128 
1129 	// make a big pitch kick with an inverse fall
1130 	ent->client->v_dmg_pitch = -40;
1131 	ent->client->v_dmg_roll = crandom() * 8;
1132 	ent->client->v_dmg_time = level.time + DAMAGE_TIME;
1133 
1134 	VectorSet(offset, 8, 8, ent->viewheight - 8);
1135 	P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
1136 	fire_bfg(ent, start, forward, damage, 400, damage_radius);
1137 
1138 	ent->client->ps.gunframe++;
1139 
1140 	PlayerNoise(ent, start, PNOISE_WEAPON);
1141 
1142 	if(!((int)dmflags->value & DF_INFINITE_AMMO))
1143 		ent->client->pers.inventory[ent->client->ammo_index] -= 50;
1144 }
1145 
Weapon_BFG(edict_t * ent)1146 void Weapon_BFG(edict_t *ent){
1147 	static int pause_frames[]	= {39, 45, 50, 55, 0};
1148 	static int fire_frames[]	= {9, 17, 0};
1149 
1150 	Weapon_Generic(ent, 8, 32, 55, 58, pause_frames, fire_frames, weapon_bfg_fire);
1151 }
1152 
1153 
1154