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