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