mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2025-01-18 14:31:55 +00:00
95295401a4
Added weapon balancing and tech control cvars to 3ZB2 DLL. Added Vampire and Ammogen techs to 3ZB2 game DLL. Fixed func_door_secret movement bug (when built with origin brush) in Zaero DLL. Fixed armor stacking bug in default Lazarus, missionpack, and Zaero DLLs. Removed unused tech cvars in default Lazarus DLL. Changed parsing of TE_BLASTER_COLORED tempent.
1572 lines
38 KiB
C
1572 lines
38 KiB
C
// g_weapon.c
|
|
|
|
#include "g_local.h"
|
|
#include "m_player.h"
|
|
|
|
|
|
qboolean is_quad;
|
|
byte is_silenced;
|
|
|
|
|
|
void playQuadSound(edict_t *ent)
|
|
{
|
|
if (ent->client->quad_framenum > level.framenum)
|
|
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
|
|
}
|
|
|
|
void weapon_grenade_fire (edict_t *ent, qboolean held);
|
|
|
|
|
|
void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)
|
|
{
|
|
vec3_t _distance;
|
|
|
|
VectorCopy (distance, _distance);
|
|
if (client->pers.hand == LEFT_HANDED)
|
|
_distance[1] *= -1;
|
|
else if (client->pers.hand == CENTER_HANDED)
|
|
_distance[1] = 0;
|
|
G_ProjectSource (point, _distance, forward, right, result);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PlayerNoise
|
|
|
|
Each player can have two noise objects associated with it:
|
|
a personal noise (jumping, pain, weapon firing), and a weapon
|
|
target noise (bullet wall impacts)
|
|
|
|
Monsters that don't directly see the player can move
|
|
to a noise in hopes of seeing the player from there.
|
|
===============
|
|
*/
|
|
void PlayerNoise(edict_t *who, vec3_t where, int type)
|
|
{
|
|
edict_t *noise;
|
|
|
|
if (type == PNOISE_WEAPON)
|
|
{
|
|
if (who->client->silencer_shots)
|
|
{
|
|
who->client->silencer_shots--;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (deathmatch->value)
|
|
return;
|
|
|
|
if (who->flags & FL_NOTARGET)
|
|
return;
|
|
|
|
|
|
if (!who->mynoise)
|
|
{
|
|
noise = G_Spawn();
|
|
noise->classname = "player_noise";
|
|
VectorSet (noise->mins, -8, -8, -8);
|
|
VectorSet (noise->maxs, 8, 8, 8);
|
|
noise->owner = who;
|
|
noise->svflags = SVF_NOCLIENT;
|
|
who->mynoise = noise;
|
|
|
|
noise = G_Spawn();
|
|
noise->classname = "player_noise";
|
|
VectorSet (noise->mins, -8, -8, -8);
|
|
VectorSet (noise->maxs, 8, 8, 8);
|
|
noise->owner = who;
|
|
noise->svflags = SVF_NOCLIENT;
|
|
who->mynoise2 = noise;
|
|
}
|
|
|
|
if (type == PNOISE_SELF || type == PNOISE_WEAPON)
|
|
{
|
|
noise = who->mynoise;
|
|
level.sound_entity = noise;
|
|
level.sound_entity_framenum = level.framenum;
|
|
}
|
|
else // type == PNOISE_IMPACT
|
|
{
|
|
noise = who->mynoise2;
|
|
level.sound2_entity = noise;
|
|
level.sound2_entity_framenum = level.framenum;
|
|
}
|
|
|
|
VectorCopy (where, noise->s.origin);
|
|
VectorSubtract (where, noise->maxs, noise->absmin);
|
|
VectorAdd (where, noise->maxs, noise->absmax);
|
|
noise->teleport_time = level.time;
|
|
gi.linkentity (noise);
|
|
}
|
|
|
|
|
|
qboolean Pickup_Weapon (edict_t *ent, edict_t *other)
|
|
{
|
|
int index;
|
|
gitem_t *ammo;
|
|
|
|
//Knightmare- override ammo pickup values with cvars
|
|
SetAmmoPickupValues ();
|
|
|
|
index = ITEM_INDEX(ent->item);
|
|
|
|
if ( ( ((int)(dmflags->value) & DF_WEAPONS_STAY) || coop->value)
|
|
&& other->client->pers.inventory[index])
|
|
{
|
|
if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM) ) )
|
|
return false; // leave the weapon for others to pickup
|
|
}
|
|
|
|
other->client->pers.inventory[index]++;
|
|
|
|
if (!(ent->spawnflags & DROPPED_ITEM) &&
|
|
(ent->item->ammo != NULL))
|
|
{
|
|
// give them some ammo with it
|
|
ammo = FindItem (ent->item->ammo);
|
|
if ( (int)dmflags->value & DF_INFINITE_AMMO )
|
|
Add_Ammo (other, ammo, 1000);
|
|
else
|
|
Add_Ammo (other, ammo, ammo->quantity);
|
|
|
|
if (! (ent->spawnflags & DROPPED_PLAYER_ITEM) )
|
|
{
|
|
if (deathmatch->value)
|
|
{
|
|
if ((int)(dmflags->value) & DF_WEAPONS_STAY)
|
|
ent->flags |= FL_RESPAWN;
|
|
else
|
|
SetRespawn (ent, 30);
|
|
}
|
|
if (coop->value)
|
|
ent->flags |= FL_RESPAWN;
|
|
}
|
|
}
|
|
|
|
if (other->client->pers.weapon != ent->item &&
|
|
!(ent->item->hideFlags & HIDE_FROM_SELECTION) &&
|
|
(other->client->pers.inventory[index] == 1) &&
|
|
( !deathmatch->value || other->client->pers.weapon == FindItem("blaster") ) )
|
|
other->client->newweapon = ent->item;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
ChangeWeapon
|
|
|
|
The old weapon has been dropped all the way, so make the new one
|
|
current
|
|
===============
|
|
*/
|
|
void ChangeWeapon (edict_t *ent)
|
|
{
|
|
int i;
|
|
#ifdef KMQUAKE2_ENGINE_MOD
|
|
int color;
|
|
#endif // KMQUAKE2_ENGINE_MOD
|
|
|
|
if (ent->client->grenade_time)
|
|
{
|
|
ent->client->grenade_time = level.time;
|
|
ent->client->weapon_sound = 0;
|
|
weapon_grenade_fire (ent, false);
|
|
ent->client->grenade_time = 0;
|
|
}
|
|
|
|
if (ent->client->pers.weapon &&
|
|
(ent->client->pers.weapon->hideFlags & HIDE_DONT_KEEP))
|
|
{
|
|
ent->client->pers.lastweapon = ent->client->pers.lastweapon2;
|
|
ent->client->pers.lastweapon2 = NULL;
|
|
ent->client->pers.weapon = ent->client->newweapon;
|
|
}
|
|
else
|
|
{
|
|
ent->client->pers.lastweapon2 = ent->client->pers.lastweapon;
|
|
ent->client->pers.lastweapon = ent->client->pers.weapon;
|
|
ent->client->pers.weapon = ent->client->newweapon;
|
|
}
|
|
ent->client->newweapon = NULL;
|
|
ent->client->machinegun_shots = 0;
|
|
|
|
// set visible model
|
|
if (ent->s.modelindex == MAX_MODELS-1)
|
|
{
|
|
if (ent->client->pers.weapon)
|
|
i = ((ent->client->pers.weapon->weapmodel & 0xff) << 8);
|
|
else
|
|
i = 0;
|
|
ent->s.skinnum = (ent - g_edicts - 1) | i;
|
|
}
|
|
|
|
if (ent->client->pers.weapon && ent->client->pers.weapon->ammo)
|
|
{
|
|
gitem_t *item = FindItem(ent->client->pers.weapon->ammo);
|
|
ent->client->ammo_index = ITEM_INDEX(item);
|
|
}
|
|
else
|
|
ent->client->ammo_index = 0;
|
|
|
|
if (!ent->client->pers.weapon)
|
|
{ // dead
|
|
ent->client->ps.gunindex = 0;
|
|
return;
|
|
}
|
|
|
|
ent->client->weaponstate = WEAPON_ACTIVATING;
|
|
ent->client->ps.gunframe = 0;
|
|
ent->client->ps.gunindex = gi.modelindex(ent->client->pers.weapon->view_model);
|
|
|
|
// Knightmare- set blaster skin based on bolt color
|
|
#ifdef KMQUAKE2_ENGINE_MOD
|
|
if (ITEM_INDEX(ent->client->pers.weapon) == blaster_index)
|
|
{
|
|
// select color
|
|
color = (int)sk_blaster_color->value;
|
|
// blaster_color could be any other value, so clamp it
|
|
if ( ((int)sk_blaster_color->value < 1) || ((int)sk_blaster_color->value > 4) )
|
|
color = BLASTER_ORANGE;
|
|
ent->client->ps.gunskin = max((color - 1), 0);
|
|
}
|
|
else if (ITEM_INDEX(ent->client->pers.weapon) == rg_index)
|
|
{
|
|
// select color
|
|
color = (int)sk_railgun_skin->value;
|
|
ent->client->ps.gunskin = min(max(color, 0), 3);
|
|
}
|
|
else
|
|
ent->client->ps.gunskin = 0;
|
|
#endif // KMQUAKE2_ENGINE_MOD
|
|
|
|
ent->client->anim_priority = ANIM_PAIN;
|
|
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
|
{
|
|
ent->s.frame = FRAME_crpain1;
|
|
ent->client->anim_end = FRAME_crpain4;
|
|
}
|
|
else
|
|
{
|
|
ent->s.frame = FRAME_pain301;
|
|
ent->client->anim_end = FRAME_pain304;
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
NoAmmoWeaponChange
|
|
=================
|
|
*/
|
|
void NoAmmoWeaponChange (edict_t *ent)
|
|
{
|
|
if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("slugs"))]
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("railgun"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("railgun");
|
|
return;
|
|
}
|
|
if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))]
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("hyperblaster"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("hyperblaster");
|
|
return;
|
|
}
|
|
if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("chaingun"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("chaingun");
|
|
return;
|
|
}
|
|
if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("machinegun"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("machinegun");
|
|
return;
|
|
}
|
|
if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))] > 1
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("super shotgun"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("super shotgun");
|
|
return;
|
|
}
|
|
if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))]
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("shotgun"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("shotgun");
|
|
return;
|
|
}
|
|
ent->client->newweapon = FindItem ("blaster");
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Think_Weapon
|
|
|
|
Called by ClientBeginServerFrame and ClientThink
|
|
=================
|
|
*/
|
|
void Think_Weapon (edict_t *ent)
|
|
{
|
|
// if just died, put the weapon away
|
|
if (ent->health < 1)
|
|
{
|
|
ent->client->newweapon = NULL;
|
|
ChangeWeapon (ent);
|
|
}
|
|
|
|
// call active weapon think routine
|
|
if (ent->client->pers.weapon && ent->client->pers.weapon->weaponthink)
|
|
{
|
|
is_quad = (ent->client->quad_framenum > level.framenum);
|
|
if (ent->client->silencer_shots)
|
|
is_silenced = MZ_SILENCED;
|
|
else
|
|
is_silenced = 0;
|
|
ent->client->pers.weapon->weaponthink (ent);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void stuffcmd(edict_t *e, char *s)
|
|
{
|
|
gi.WriteByte (11);
|
|
gi.WriteString (s);
|
|
gi.unicast (e, true);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
Use_Weapon
|
|
|
|
Make the weapon ready if there is ammo
|
|
================
|
|
*/
|
|
void Use_Weapon (edict_t *ent, gitem_t *item)
|
|
{
|
|
int ammo_index;
|
|
gitem_t *ammo_item;
|
|
|
|
// see if we're already using it
|
|
if (item == ent->client->pers.weapon)
|
|
{
|
|
return;
|
|
}
|
|
if (item->ammo && !g_select_empty->value && !(item->flags & IT_AMMO))
|
|
{
|
|
ammo_item = FindItem(item->ammo);
|
|
if (ammo_item != NULL)
|
|
{
|
|
ammo_index = ITEM_INDEX(ammo_item);
|
|
|
|
if (!ent->client->pers.inventory[ammo_index])
|
|
{
|
|
gi.cprintf (ent, PRINT_HIGH, "No %s for %s.\n", ammo_item->pickup_name, item->pickup_name);
|
|
return;
|
|
}
|
|
|
|
if (ent->client->pers.inventory[ammo_index] < item->quantity)
|
|
{
|
|
gi.cprintf (ent, PRINT_HIGH, "Not enough %s for %s.\n", ammo_item->pickup_name, item->pickup_name);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
ammo_index = 0;
|
|
}
|
|
|
|
// change to this weapon when down
|
|
ent->client->newweapon = item;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
================
|
|
Drop_Weapon
|
|
================
|
|
*/
|
|
void Drop_Weapon (edict_t *ent, gitem_t *item)
|
|
{
|
|
int index;
|
|
|
|
if ((int)(dmflags->value) & DF_WEAPONS_STAY)
|
|
return;
|
|
|
|
index = ITEM_INDEX(item);
|
|
// see if we're already using it
|
|
if ( ((item == ent->client->pers.weapon) || (item == ent->client->newweapon))&& (ent->client->pers.inventory[index] == 1) )
|
|
{
|
|
gi.cprintf (ent, PRINT_HIGH, "Can't drop current weapon\n");
|
|
return;
|
|
}
|
|
|
|
Drop_Item (ent, item);
|
|
ent->client->pers.inventory[index]--;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
Weapon_Generic
|
|
|
|
A generic function to handle the basics of weapon thinking
|
|
================
|
|
*/
|
|
#define FRAME_FIRE_FIRST (FRAME_ACTIVATE_LAST + 1)
|
|
#define FRAME_IDLE_FIRST (FRAME_FIRE_LAST + 1)
|
|
#define FRAME_DEACTIVATE_FIRST (FRAME_IDLE_LAST + 1)
|
|
|
|
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, qboolean altfire))
|
|
{
|
|
int n;
|
|
|
|
if (ent->client->weaponstate == WEAPON_DROPPING)
|
|
{
|
|
if (ent->client->ps.gunframe == FRAME_DEACTIVATE_LAST)
|
|
{
|
|
ChangeWeapon (ent);
|
|
return;
|
|
}
|
|
|
|
ent->client->ps.gunframe++;
|
|
return;
|
|
}
|
|
|
|
if (ent->client->weaponstate == WEAPON_ACTIVATING)
|
|
{
|
|
if (ent->client->ps.gunframe == FRAME_ACTIVATE_LAST)
|
|
{
|
|
ent->client->weaponstate = WEAPON_READY;
|
|
ent->client->ps.gunframe = FRAME_IDLE_FIRST;
|
|
return;
|
|
}
|
|
|
|
ent->client->ps.gunframe++;
|
|
return;
|
|
}
|
|
|
|
if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING))
|
|
{
|
|
ent->client->weaponstate = WEAPON_DROPPING;
|
|
ent->client->ps.gunframe = FRAME_DEACTIVATE_FIRST;
|
|
return;
|
|
}
|
|
|
|
if (ent->client->weaponstate == WEAPON_READY)
|
|
{
|
|
if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTONS_ATTACK) )
|
|
{
|
|
ent->client->latched_buttons &= ~BUTTONS_ATTACK;
|
|
if ((!ent->client->ammo_index) ||
|
|
( ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity))
|
|
{
|
|
ent->client->ps.gunframe = FRAME_FIRE_FIRST;
|
|
ent->client->weaponstate = WEAPON_FIRING;
|
|
|
|
// start the animation
|
|
ent->client->anim_priority = ANIM_ATTACK;
|
|
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
|
{
|
|
ent->s.frame = FRAME_crattak1-1;
|
|
ent->client->anim_end = FRAME_crattak9;
|
|
}
|
|
else
|
|
{
|
|
ent->s.frame = FRAME_attack1-1;
|
|
ent->client->anim_end = FRAME_attack8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (level.time >= ent->pain_debounce_time)
|
|
{
|
|
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
|
|
ent->pain_debounce_time = level.time + 1;
|
|
}
|
|
NoAmmoWeaponChange (ent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ent->client->ps.gunframe == FRAME_IDLE_LAST)
|
|
{
|
|
ent->client->ps.gunframe = FRAME_IDLE_FIRST;
|
|
return;
|
|
}
|
|
|
|
if (pause_frames)
|
|
{
|
|
for (n = 0; pause_frames[n]; n++)
|
|
{
|
|
if (ent->client->ps.gunframe == pause_frames[n])
|
|
{
|
|
if (rand()&15)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
ent->client->ps.gunframe++;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ent->client->weaponstate == WEAPON_FIRING)
|
|
{
|
|
for (n = 0; fire_frames[n]; n++)
|
|
{
|
|
if (ent->client->ps.gunframe == fire_frames[n])
|
|
{
|
|
#if 0
|
|
// TODO move to each of weapon firing functions so that the quad
|
|
// sound is only played when we actually want it to, instead of
|
|
// every firing frame
|
|
if (ent->client->quad_framenum > level.framenum)
|
|
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
|
|
#endif
|
|
fire (ent, ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK2) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!fire_frames[n])
|
|
ent->client->ps.gunframe++;
|
|
|
|
if (ent->client->ps.gunframe == FRAME_IDLE_FIRST+1)
|
|
ent->client->weaponstate = WEAPON_READY;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
GRENADE
|
|
|
|
======================================================================
|
|
*/
|
|
|
|
#define GRENADE_TIMER 3.0
|
|
#define GRENADE_MINSPEED 400
|
|
#define GRENADE_MAXSPEED 800
|
|
|
|
void weapon_grenade_fire (edict_t *ent, qboolean held)
|
|
{
|
|
vec3_t offset;
|
|
vec3_t forward, right;
|
|
vec3_t start;
|
|
int damage = (int)sk_hand_grenade_damage->value;
|
|
float timer;
|
|
int speed;
|
|
float radius;
|
|
|
|
// radius = damage + 40;
|
|
radius = sk_hand_grenade_radius->value; // was damage + 40
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
|
|
timer = ent->client->grenade_time - level.time;
|
|
speed = GRENADE_MINSPEED + (GRENADE_TIMER - timer) * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER);
|
|
fire_grenade2 (ent, start, forward, damage, speed, timer, radius, held);
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
|
|
|
ent->client->grenade_time = level.time + 1.0;
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
|
|
void Weapon_Grenade (edict_t *ent)
|
|
{
|
|
if ((ent->client->newweapon) && (ent->client->weaponstate == WEAPON_READY))
|
|
{
|
|
ChangeWeapon (ent);
|
|
return;
|
|
}
|
|
|
|
if (ent->client->weaponstate == WEAPON_ACTIVATING)
|
|
{
|
|
ent->client->weaponstate = WEAPON_READY;
|
|
ent->client->ps.gunframe = 16;
|
|
return;
|
|
}
|
|
|
|
if (ent->client->weaponstate == WEAPON_READY)
|
|
{
|
|
if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTONS_ATTACK) )
|
|
{
|
|
ent->client->latched_buttons &= ~BUTTONS_ATTACK;
|
|
if (ent->client->pers.inventory[ent->client->ammo_index])
|
|
{
|
|
ent->client->ps.gunframe = 1;
|
|
ent->client->weaponstate = WEAPON_FIRING;
|
|
ent->client->grenade_time = 0;
|
|
}
|
|
else
|
|
{
|
|
if (level.time >= ent->pain_debounce_time)
|
|
{
|
|
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
|
|
ent->pain_debounce_time = level.time + 1;
|
|
}
|
|
NoAmmoWeaponChange (ent);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((ent->client->ps.gunframe == 29) || (ent->client->ps.gunframe == 34) || (ent->client->ps.gunframe == 39) || (ent->client->ps.gunframe == 48))
|
|
{
|
|
if (rand()&15)
|
|
return;
|
|
}
|
|
|
|
if (++ent->client->ps.gunframe > 48)
|
|
ent->client->ps.gunframe = 16;
|
|
return;
|
|
}
|
|
|
|
if (ent->client->weaponstate == WEAPON_FIRING)
|
|
{
|
|
if (ent->client->ps.gunframe == 5)
|
|
gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/hgrena1b.wav"), 1, ATTN_NORM, 0);
|
|
|
|
if (ent->client->ps.gunframe == 11)
|
|
{
|
|
if (!ent->client->grenade_time)
|
|
{
|
|
ent->client->grenade_time = level.time + GRENADE_TIMER + 0.2;
|
|
ent->client->weapon_sound = gi.soundindex("weapons/hgrenc1b.wav");
|
|
}
|
|
|
|
// they waited too long, detonate it in their hand
|
|
if (!ent->client->grenade_blew_up && level.time >= ent->client->grenade_time)
|
|
{
|
|
ent->client->weapon_sound = 0;
|
|
weapon_grenade_fire (ent, true);
|
|
ent->client->grenade_blew_up = true;
|
|
}
|
|
|
|
if (ent->client->buttons & BUTTONS_ATTACK)
|
|
return;
|
|
|
|
if (ent->client->grenade_blew_up)
|
|
{
|
|
if (level.time >= ent->client->grenade_time)
|
|
{
|
|
ent->client->ps.gunframe = 15;
|
|
ent->client->grenade_blew_up = false;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ent->client->ps.gunframe == 12)
|
|
{
|
|
ent->client->weapon_sound = 0;
|
|
weapon_grenade_fire (ent, false);
|
|
}
|
|
|
|
if ((ent->client->ps.gunframe == 15) && (level.time < ent->client->grenade_time))
|
|
return;
|
|
|
|
ent->client->ps.gunframe++;
|
|
|
|
if (ent->client->ps.gunframe == 16)
|
|
{
|
|
ent->client->grenade_time = 0;
|
|
ent->client->weaponstate = WEAPON_READY;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
GRENADE LAUNCHER
|
|
|
|
======================================================================
|
|
*/
|
|
|
|
void weapon_grenadelauncher_fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
vec3_t offset;
|
|
vec3_t forward, right;
|
|
vec3_t start;
|
|
int damage;
|
|
float radius;
|
|
|
|
if (GetItemByIndex(ent->client->ammo_index)->tag == AMMO_GRENADES)
|
|
damage = (int)sk_grenade_damage->value;
|
|
else
|
|
damage = 300;
|
|
|
|
radius = sk_grenade_radius->value; // damage + 40
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
|
|
VectorScale (forward, -2, ent->client->kick_origin);
|
|
ent->client->kick_angles[0] = -1;
|
|
|
|
fire_grenade (ent, start, forward, damage, (int)sk_grenade_speed->value, 2.5, radius, altfire);
|
|
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (MZ_GRENADE | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
ent->client->ps.gunframe++;
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
|
|
void Weapon_GrenadeLauncher (edict_t *ent)
|
|
{
|
|
static int pause_frames[] = {34, 51, 59, 0};
|
|
static int fire_frames[] = {6, 0};
|
|
|
|
Weapon_Generic (ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_grenadelauncher_fire);
|
|
}
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
ROCKET
|
|
|
|
======================================================================
|
|
*/
|
|
void Weapon_RocketLauncher_Fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
vec3_t offset, start;
|
|
vec3_t forward, right;
|
|
int damage;
|
|
float damage_radius;
|
|
int radius_damage;
|
|
|
|
damage = (int)sk_rocket_damage->value + (int)(random() * (int)sk_rocket_damage2->value);
|
|
radius_damage = (int)sk_rocket_rdamage->value;
|
|
damage_radius = sk_rocket_radius->value;
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
radius_damage *= 4;
|
|
}
|
|
|
|
ent->client->ps.gunframe++;
|
|
|
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
|
|
|
VectorScale (forward, -2, ent->client->kick_origin);
|
|
ent->client->kick_angles[0] = -1;
|
|
|
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
|
|
if (EMPNukeCheck(ent, start))
|
|
{
|
|
gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
|
|
}
|
|
else
|
|
{
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
|
|
|
fire_rocket (ent, start, forward, damage, (int)sk_rocket_speed->value, damage_radius, radius_damage);
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (MZ_ROCKET | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
}
|
|
|
|
void Weapon_RocketLauncher (edict_t *ent)
|
|
{
|
|
static int pause_frames[] = {25, 33, 42, 50, 0};
|
|
static int fire_frames[] = {5, 0};
|
|
|
|
Weapon_Generic (ent, 4, 12, 50, 54, pause_frames, fire_frames, Weapon_RocketLauncher_Fire);
|
|
}
|
|
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
BLASTER / HYPERBLASTER
|
|
|
|
======================================================================
|
|
*/
|
|
|
|
int Blaster_Fire (edict_t *ent, vec3_t g_offset, int damage, qboolean hyper, int effect, int color)
|
|
{
|
|
vec3_t forward, right;
|
|
vec3_t start, offset;
|
|
int muzzleflash, ret = 1;
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
|
VectorSet(offset, 24, 8, ent->viewheight-8);
|
|
VectorAdd (offset, g_offset, offset);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
|
|
VectorScale (forward, -2, ent->client->kick_origin);
|
|
ent->client->kick_angles[0] = -1;
|
|
|
|
if (EMPNukeCheck(ent, start))
|
|
{
|
|
gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
|
|
ret = 0;
|
|
}
|
|
else
|
|
{
|
|
if (!hyper)
|
|
fire_blaster (ent, start, forward, damage, (int)sk_blaster_speed->value, effect, hyper, color);
|
|
else
|
|
fire_blaster (ent, start, forward, damage, (int)sk_hyperblaster_speed->value, effect, hyper, color);
|
|
// fire_blaster (ent, start, forward, damage, 1000, effect, hyper, BLASTER_ORANGE);
|
|
|
|
// Knightmare- select muzzle flash
|
|
if (hyper)
|
|
{
|
|
if (color == BLASTER_GREEN)
|
|
#ifdef KMQUAKE2_ENGINE_MOD
|
|
muzzleflash = MZ_GREENHYPERBLASTER;
|
|
#else
|
|
muzzleflash = MZ_HYPERBLASTER;
|
|
#endif
|
|
else if (color == BLASTER_BLUE)
|
|
muzzleflash = MZ_BLUEHYPERBLASTER;
|
|
#ifdef KMQUAKE2_ENGINE_MOD
|
|
else if (color == BLASTER_RED)
|
|
muzzleflash = MZ_REDHYPERBLASTER;
|
|
#endif
|
|
else //standard orange
|
|
muzzleflash = MZ_HYPERBLASTER;
|
|
}
|
|
else
|
|
{
|
|
if (color == BLASTER_GREEN)
|
|
muzzleflash = MZ_BLASTER2;
|
|
else if (color == BLASTER_BLUE)
|
|
#ifdef KMQUAKE2_ENGINE_MOD
|
|
muzzleflash = MZ_BLUEBLASTER;
|
|
#else
|
|
muzzleflash = MZ_BLASTER;
|
|
#endif
|
|
#ifdef KMQUAKE2_ENGINE_MOD
|
|
else if (color == BLASTER_RED)
|
|
muzzleflash = MZ_REDBLASTER;
|
|
#endif
|
|
else //standard orange
|
|
muzzleflash = MZ_BLASTER;
|
|
}
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (muzzleflash | is_silenced);
|
|
// if (hyper)
|
|
// gi.WriteByte (MZ_HYPERBLASTER | is_silenced);
|
|
// else
|
|
// gi.WriteByte (MZ_BLASTER | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void Weapon_Blaster_Fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
int damage, effect, color;
|
|
|
|
if (deathmatch->value)
|
|
damage = (int)sk_blaster_damage_dm->value;
|
|
else
|
|
damage = (int)sk_blaster_damage->value;
|
|
|
|
// select color
|
|
color = (int)sk_blaster_color->value;
|
|
// blaster_color could be any other value, so clamp it
|
|
if ( ((int)sk_blaster_color->value < 1) || ((int)sk_blaster_color->value > 4) )
|
|
color = BLASTER_ORANGE;
|
|
#ifndef KMQUAKE2_ENGINE_MOD
|
|
if (color == BLASTER_RED) color = BLASTER_ORANGE;
|
|
#endif
|
|
|
|
if (color == BLASTER_GREEN)
|
|
effect = (EF_BLASTER|EF_TRACKER);
|
|
else if (color == BLASTER_BLUE)
|
|
#ifdef KMQUAKE2_ENGINE_MOD
|
|
effect = EF_BLASTER|EF_BLUEHYPERBLASTER;
|
|
#else
|
|
effect = EF_BLUEHYPERBLASTER;
|
|
#endif
|
|
else if (color == BLASTER_RED)
|
|
effect = EF_BLASTER|EF_IONRIPPER;
|
|
else // standard orange
|
|
effect = EF_BLASTER;
|
|
|
|
Blaster_Fire (ent, vec3_origin, damage, false, effect, color);
|
|
// Blaster_Fire (ent, vec3_origin, damage, false, EF_BLASTER);
|
|
ent->client->ps.gunframe++;
|
|
}
|
|
|
|
void Weapon_Blaster (edict_t *ent)
|
|
{
|
|
static int pause_frames[] = {19, 32, 0};
|
|
static int fire_frames[] = {5, 0};
|
|
|
|
Weapon_Generic (ent, 4, 8, 52, 55, pause_frames, fire_frames, Weapon_Blaster_Fire);
|
|
}
|
|
|
|
|
|
void Weapon_HyperBlaster_Fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
float rotation;
|
|
vec3_t offset;
|
|
int effect, damage, color;
|
|
|
|
ent->client->weapon_sound = gi.soundindex("weapons/hyprbl1a.wav");
|
|
|
|
if (!(ent->client->buttons & BUTTONS_ATTACK))
|
|
{
|
|
ent->client->ps.gunframe++;
|
|
}
|
|
else
|
|
{
|
|
if (! ent->client->pers.inventory[ent->client->ammo_index] )
|
|
{
|
|
if (level.time >= ent->pain_debounce_time)
|
|
{
|
|
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
|
|
ent->pain_debounce_time = level.time + 1;
|
|
}
|
|
NoAmmoWeaponChange (ent);
|
|
}
|
|
else
|
|
{
|
|
rotation = (ent->client->ps.gunframe - 5) * 2*M_PI/6;
|
|
offset[0] = -4 * sin(rotation);
|
|
offset[1] = 0;
|
|
offset[2] = 4 * cos(rotation);
|
|
|
|
// Knightmare- select color
|
|
color = (int)sk_hyperblaster_color->value;
|
|
// hyperblaster_color could be any other value, so clamp it
|
|
if ( ((int)sk_hyperblaster_color->value < 1) || ((int)sk_hyperblaster_color->value > 4) )
|
|
color = BLASTER_ORANGE;
|
|
#ifndef KMQUAKE2_ENGINE_MOD
|
|
if (color == BLASTER_RED) color = BLASTER_ORANGE;
|
|
#endif
|
|
if ((ent->client->ps.gunframe == 6) || (ent->client->ps.gunframe == 9))
|
|
{
|
|
if (color == BLASTER_GREEN)
|
|
effect = (EF_HYPERBLASTER|EF_TRACKER);
|
|
else if (color == BLASTER_BLUE)
|
|
effect = EF_BLUEHYPERBLASTER;
|
|
else if (color == BLASTER_RED)
|
|
effect = EF_HYPERBLASTER|EF_IONRIPPER;
|
|
else // standard orange
|
|
effect = EF_HYPERBLASTER;
|
|
}
|
|
else
|
|
effect = 0;
|
|
|
|
if (deathmatch->value)
|
|
damage = (int)sk_hyperblaster_damage_dm->value;
|
|
else
|
|
damage = (int)sk_hyperblaster_damage->value;
|
|
|
|
if (Blaster_Fire (ent, offset, damage, true, effect, color))
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
|
|
ent->client->ps.gunframe++;
|
|
if (ent->client->ps.gunframe == 12 && ent->client->pers.inventory[ent->client->ammo_index])
|
|
ent->client->ps.gunframe = 6;
|
|
}
|
|
|
|
if (ent->client->ps.gunframe == 12)
|
|
{
|
|
gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/hyprbd1a.wav"), 1, ATTN_NORM, 0);
|
|
ent->client->weapon_sound = 0;
|
|
}
|
|
|
|
}
|
|
|
|
void Weapon_HyperBlaster (edict_t *ent, qboolean altfire)
|
|
{
|
|
static int pause_frames[] = {0};
|
|
static int fire_frames[] = {6, 7, 8, 9, 10, 11, 0};
|
|
|
|
Weapon_Generic (ent, 5, 20, 49, 53, pause_frames, fire_frames, Weapon_HyperBlaster_Fire);
|
|
}
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
MACHINEGUN / CHAINGUN
|
|
|
|
======================================================================
|
|
*/
|
|
|
|
void Machinegun_Fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
int i;
|
|
vec3_t start;
|
|
vec3_t forward, right;
|
|
vec3_t angles;
|
|
int damage = (int)sk_machinegun_damage->value;
|
|
int kick = 2;
|
|
vec3_t offset;
|
|
|
|
if (!(ent->client->buttons & BUTTONS_ATTACK))
|
|
{
|
|
ent->client->machinegun_shots = 0;
|
|
ent->client->ps.gunframe++;
|
|
return;
|
|
}
|
|
|
|
if (ent->client->ps.gunframe == 5)
|
|
ent->client->ps.gunframe = 4;
|
|
else
|
|
ent->client->ps.gunframe = 5;
|
|
|
|
if (ent->client->pers.inventory[ent->client->ammo_index] < 1)
|
|
{
|
|
ent->client->ps.gunframe = 6;
|
|
if (level.time >= ent->pain_debounce_time)
|
|
{
|
|
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
|
|
ent->pain_debounce_time = level.time + 1;
|
|
}
|
|
NoAmmoWeaponChange (ent);
|
|
return;
|
|
}
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
kick *= 4;
|
|
}
|
|
|
|
for (i=1 ; i<3 ; i++)
|
|
{
|
|
ent->client->kick_origin[i] = crandom() * 0.35;
|
|
ent->client->kick_angles[i] = crandom() * 0.7;
|
|
}
|
|
ent->client->kick_origin[0] = crandom() * 0.35;
|
|
ent->client->kick_angles[0] = ent->client->machinegun_shots * -1.5;
|
|
|
|
// raise the gun as it is firing
|
|
if (!deathmatch->value)
|
|
{
|
|
ent->client->machinegun_shots++;
|
|
if (ent->client->machinegun_shots > 9)
|
|
ent->client->machinegun_shots = 9;
|
|
}
|
|
|
|
// get start / end positions
|
|
VectorAdd (ent->client->v_angle, ent->client->kick_angles, angles);
|
|
AngleVectors (angles, forward, right, NULL);
|
|
VectorSet(offset, 0, 8, ent->viewheight-8);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
fire_bullet (ent, start, forward, damage, kick, (int)sk_machinegun_hspread->value, (int)sk_machinegun_vspread->value, MOD_MACHINEGUN);
|
|
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (MZ_MACHINEGUN | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
|
|
void Weapon_Machinegun (edict_t *ent)
|
|
{
|
|
static int pause_frames[] = {23, 45, 0};
|
|
static int fire_frames[] = {4, 5, 0};
|
|
|
|
Weapon_Generic (ent, 3, 5, 45, 49, pause_frames, fire_frames, Machinegun_Fire);
|
|
}
|
|
|
|
void Chaingun_Fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
int i;
|
|
int shots;
|
|
vec3_t start;
|
|
vec3_t forward, right, up;
|
|
float r, u;
|
|
vec3_t offset;
|
|
int damage;
|
|
int kick = 2;
|
|
|
|
if (deathmatch->value)
|
|
damage = (int)sk_chaingun_damage_dm->value;
|
|
else
|
|
damage = (int)sk_chaingun_damage->value;
|
|
|
|
if (ent->client->ps.gunframe == 5)
|
|
gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnu1a.wav"), 1, ATTN_IDLE, 0);
|
|
|
|
if ((ent->client->ps.gunframe == 14) && !(ent->client->buttons & BUTTONS_ATTACK))
|
|
{
|
|
ent->client->ps.gunframe = 32;
|
|
ent->client->weapon_sound = 0;
|
|
return;
|
|
}
|
|
else if ((ent->client->ps.gunframe == 21) && (ent->client->buttons & BUTTONS_ATTACK)
|
|
&& ent->client->pers.inventory[ent->client->ammo_index])
|
|
{
|
|
ent->client->ps.gunframe = 15;
|
|
}
|
|
else
|
|
{
|
|
ent->client->ps.gunframe++;
|
|
}
|
|
|
|
if (ent->client->ps.gunframe == 22)
|
|
{
|
|
ent->client->weapon_sound = 0;
|
|
gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnd1a.wav"), 1, ATTN_IDLE, 0);
|
|
}
|
|
else
|
|
{
|
|
ent->client->weapon_sound = gi.soundindex("weapons/chngnl1a.wav");
|
|
}
|
|
|
|
if (ent->client->ps.gunframe <= 9)
|
|
shots = 1;
|
|
else if (ent->client->ps.gunframe <= 14)
|
|
{
|
|
if (ent->client->buttons & BUTTONS_ATTACK)
|
|
shots = 2;
|
|
else
|
|
shots = 1;
|
|
}
|
|
else
|
|
shots = 3;
|
|
|
|
if (ent->client->pers.inventory[ent->client->ammo_index] < shots)
|
|
shots = ent->client->pers.inventory[ent->client->ammo_index];
|
|
|
|
if (!shots)
|
|
{
|
|
if (level.time >= ent->pain_debounce_time)
|
|
{
|
|
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
|
|
ent->pain_debounce_time = level.time + 1;
|
|
}
|
|
NoAmmoWeaponChange (ent);
|
|
return;
|
|
}
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
kick *= 4;
|
|
}
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
ent->client->kick_origin[i] = crandom() * 0.35;
|
|
ent->client->kick_angles[i] = crandom() * 0.7;
|
|
}
|
|
|
|
if (EMPNukeCheck(ent, ent->s.origin))
|
|
{
|
|
gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
|
|
return;
|
|
}
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index] -= shots;
|
|
|
|
for (i=0 ; i<shots ; i++)
|
|
{
|
|
// get start / end positions
|
|
AngleVectors (ent->client->v_angle, forward, right, up);
|
|
r = 7 + crandom()*4;
|
|
u = crandom()*4;
|
|
VectorSet(offset, 0, r, u + ent->viewheight-8);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
|
|
fire_bullet (ent, start, forward, damage, kick, (int)sk_chaingun_hspread->value, (int)sk_chaingun_vspread->value, MOD_CHAINGUN);
|
|
}
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte ((MZ_CHAINGUN1 + shots - 1) | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
|
|
|
|
void Weapon_Chaingun (edict_t *ent)
|
|
{
|
|
static int pause_frames[] = {38, 43, 51, 61, 0};
|
|
static int fire_frames[] = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0};
|
|
|
|
Weapon_Generic (ent, 4, 31, 61, 64, pause_frames, fire_frames, Chaingun_Fire);
|
|
}
|
|
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
SHOTGUN / SUPERSHOTGUN
|
|
|
|
======================================================================
|
|
*/
|
|
|
|
void weapon_shotgun_fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
vec3_t start;
|
|
vec3_t forward, right;
|
|
vec3_t offset;
|
|
int damage = (int)sk_shotgun_damage->value;
|
|
int kick = 8;
|
|
|
|
if (ent->client->ps.gunframe == 9)
|
|
{
|
|
ent->client->ps.gunframe++;
|
|
return;
|
|
}
|
|
|
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
|
|
|
VectorScale (forward, -2, ent->client->kick_origin);
|
|
ent->client->kick_angles[0] = -2;
|
|
|
|
VectorSet(offset, 0, 8, ent->viewheight-8);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
kick *= 4;
|
|
}
|
|
|
|
if (deathmatch->value)
|
|
fire_shotgun (ent, start, forward, damage, kick, (int)sk_shotgun_hspread->value, (int)sk_shotgun_vspread->value, (int)sk_shotgun_count->value, MOD_SHOTGUN);
|
|
else
|
|
fire_shotgun (ent, start, forward, damage, kick, (int)sk_shotgun_hspread->value, (int)sk_shotgun_vspread->value, (int)sk_shotgun_count->value, MOD_SHOTGUN);
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (MZ_SHOTGUN | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
ent->client->ps.gunframe++;
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
|
|
void Weapon_Shotgun (edict_t *ent)
|
|
{
|
|
static int pause_frames[] = {22, 28, 34, 0};
|
|
static int fire_frames[] = {8, 9, 0};
|
|
|
|
Weapon_Generic (ent, 7, 18, 36, 39, pause_frames, fire_frames, weapon_shotgun_fire);
|
|
}
|
|
|
|
|
|
void weapon_supershotgun_fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
vec3_t start;
|
|
vec3_t forward, right;
|
|
vec3_t offset;
|
|
vec3_t v;
|
|
int damage = (int)sk_sshotgun_damage->value;
|
|
int kick = 12;
|
|
|
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
|
|
|
VectorScale (forward, -2, ent->client->kick_origin);
|
|
ent->client->kick_angles[0] = -2;
|
|
|
|
VectorSet(offset, 0, 8, ent->viewheight-8);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
kick *= 4;
|
|
}
|
|
|
|
v[PITCH] = ent->client->v_angle[PITCH];
|
|
v[YAW] = ent->client->v_angle[YAW] - 5;
|
|
v[ROLL] = ent->client->v_angle[ROLL];
|
|
AngleVectors (v, forward, NULL, NULL);
|
|
fire_shotgun (ent, start, forward, damage, kick, (int)sk_sshotgun_hspread->value, (int)sk_sshotgun_vspread->value, (int)sk_sshotgun_count->value/2, MOD_SSHOTGUN);
|
|
v[YAW] = ent->client->v_angle[YAW] + 5;
|
|
AngleVectors (v, forward, NULL, NULL);
|
|
fire_shotgun (ent, start, forward, damage, kick, (int)sk_sshotgun_hspread->value, (int)sk_sshotgun_vspread->value, (int)sk_sshotgun_count->value/2, MOD_SSHOTGUN);
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (MZ_SSHOTGUN | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
ent->client->ps.gunframe++;
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index] -= 2;
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
|
|
void Weapon_SuperShotgun (edict_t *ent)
|
|
{
|
|
static int pause_frames[] = {29, 42, 57, 0};
|
|
static int fire_frames[] = {7, 0};
|
|
|
|
Weapon_Generic (ent, 6, 17, 57, 61, pause_frames, fire_frames, weapon_supershotgun_fire);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
RAILGUN
|
|
|
|
======================================================================
|
|
*/
|
|
|
|
void weapon_railgun_fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
vec3_t start;
|
|
vec3_t forward, right;
|
|
vec3_t offset;
|
|
int damage;
|
|
int kick;
|
|
int red=20, green=48, blue=176;
|
|
qboolean useColor=false;
|
|
|
|
if (deathmatch->value)
|
|
{ // normal damage is too extreme in dm
|
|
damage = (int)sk_railgun_damage_dm->value;
|
|
kick = 200;
|
|
}
|
|
else
|
|
{
|
|
damage = (int)sk_railgun_damage->value;
|
|
kick = 250;
|
|
}
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
kick *= 4;
|
|
}
|
|
|
|
// custom client color
|
|
if ( ent->client && (ent->client->pers.color1[3] != 0) )
|
|
{
|
|
useColor = true;
|
|
red = ent->client->pers.color1[0];
|
|
green = ent->client->pers.color1[1];
|
|
blue = ent->client->pers.color1[2];
|
|
}
|
|
|
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
|
|
|
VectorScale (forward, -3, ent->client->kick_origin);
|
|
ent->client->kick_angles[0] = -3;
|
|
|
|
VectorSet(offset, 0, 7, ent->viewheight-8);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
|
|
ent->client->ps.gunframe++;
|
|
|
|
if (EMPNukeCheck(ent, start))
|
|
{
|
|
gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
|
|
return;
|
|
}
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
|
|
|
fire_rail (ent, start, forward, damage, kick, useColor, red, green, blue);
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (MZ_RAILGUN | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
|
|
|
|
void Weapon_Railgun (edict_t *ent)
|
|
{
|
|
static int pause_frames[] = {56, 0};
|
|
static int fire_frames[] = {4, 0};
|
|
|
|
Weapon_Generic (ent, 3, 18, 56, 61, pause_frames, fire_frames, weapon_railgun_fire);
|
|
}
|
|
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
BFG10K
|
|
|
|
======================================================================
|
|
*/
|
|
|
|
void weapon_bfg_fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
vec3_t offset, start;
|
|
vec3_t forward, right;
|
|
int damage;
|
|
float damage_radius = sk_bfg_radius->value;
|
|
|
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
|
VectorScale (forward, -2, ent->client->kick_origin);
|
|
|
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
|
|
if(ent->client->ps.gunframe == 9)
|
|
{
|
|
ent->flags &= ~FL_BFGMISSFIRE;
|
|
}
|
|
|
|
if (!(ent->flags & FL_BFGMISSFIRE) && EMPNukeCheck(ent, start))
|
|
{
|
|
ent->flags |= FL_BFGMISSFIRE;
|
|
gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
|
|
}
|
|
|
|
if(ent->flags & FL_BFGMISSFIRE)
|
|
{
|
|
ent->client->ps.gunframe++;
|
|
return;
|
|
}
|
|
|
|
if (deathmatch->value)
|
|
damage = (int)sk_bfg_damage_dm->value;
|
|
else
|
|
damage = (int)sk_bfg_damage->value;
|
|
|
|
if (ent->client->ps.gunframe == 9)
|
|
{
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (MZ_BFG | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
ent->client->ps.gunframe++;
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
return;
|
|
}
|
|
|
|
// cells can go down during windup (from power armor hits), so
|
|
// check again and abort firing if we don't have enough now
|
|
if (ent->client->pers.inventory[ent->client->ammo_index] < 50)
|
|
{
|
|
ent->client->ps.gunframe++;
|
|
return;
|
|
}
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
// make a big pitch kick with an inverse fall
|
|
ent->client->v_dmg_pitch = -40;
|
|
ent->client->v_dmg_roll = crandom()*8;
|
|
ent->client->v_dmg_time = level.time + DAMAGE_TIME;
|
|
|
|
ent->client->ps.gunframe++;
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index] -= 50;
|
|
|
|
fire_bfg (ent, start, forward, damage, (int)sk_bfg_speed->value, damage_radius);
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
// play quad damage sound
|
|
playQuadSound(ent);
|
|
}
|
|
|
|
void Weapon_BFG (edict_t *ent)
|
|
{
|
|
static int pause_frames[] = {39, 45, 50, 55, 0};
|
|
static int fire_frames[] = {9, 17, 0};
|
|
|
|
Weapon_Generic (ent, 8, 32, 55, 58, pause_frames, fire_frames, weapon_bfg_fire);
|
|
}
|
|
|
|
|
|
//======================================================================
|