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