p_weapon.c (34565B)
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 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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, ent->s.origin, 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 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 //======================================================================