mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-14 16:40:57 +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.
1924 lines
50 KiB
C
1924 lines
50 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
Copyright (C) 2000-2002 Mr. Hyde and Mad Dog
|
|
|
|
This file is part of Lazarus Quake 2 Mod source code.
|
|
|
|
Lazarus Quake 2 Mod source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Lazarus Quake 2 Mod source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Lazarus Quake 2 Mod source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// g_weapon.c
|
|
|
|
#include "g_local.h"
|
|
#include "m_player.h"
|
|
|
|
|
|
static qboolean is_quad;
|
|
static byte is_silenced;
|
|
|
|
|
|
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);
|
|
// DWH
|
|
//if (client->zoomed)
|
|
// _distance[2] += 8;
|
|
// end DWH
|
|
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->flags & FL_DISGUISED)
|
|
{
|
|
if (type == PNOISE_WEAPON)
|
|
{
|
|
level.disguise_violator = who;
|
|
level.disguise_violation_framenum = level.framenum + 5;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (!who->mynoise)
|
|
{
|
|
noise = G_Spawn();
|
|
noise->classname = "player_noise";
|
|
noise->class_id = ENTITY_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";
|
|
noise->class_id = ENTITY_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) )
|
|
{
|
|
// give them some ammo with it
|
|
|
|
// Lazarus: blaster doesn't use ammo
|
|
if (ent->item->ammo)
|
|
{
|
|
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 &&
|
|
(other->client->pers.inventory[index] == 1) &&
|
|
( !deathmatch->value || other->client->pers.weapon == FindItem("blaster") ||
|
|
other->client->pers.weapon == FindItem("No weapon") ) )
|
|
other->client->newweapon = ent->item;
|
|
|
|
// If rocket launcher, give the HML (but no ammo).
|
|
if (index == rl_index)
|
|
other->client->pers.inventory[hml_index] = other->client->pers.inventory[index];
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
ent->client->ammo_index = ITEM_INDEX(FindItem(ent->client->pers.weapon->ammo));
|
|
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;
|
|
|
|
// DWH: Don't display weapon if in 3rd person
|
|
if (!ent->client->chasetoggle)
|
|
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;
|
|
// CTF color override
|
|
if ( (int)ctf->value && (int)ctf_blastercolors->value && ent->client )
|
|
color = (5 - ent->client->resp.ctf_team);
|
|
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;
|
|
// CTF color override
|
|
if ( (int)ctf->value && (int)ctf_railcolors->value && ent->client )
|
|
color = (ent->client->resp.ctf_team - 1);
|
|
ent->client->ps.gunskin = min(max(color, 0), 3);
|
|
}
|
|
else
|
|
ent->client->ps.gunskin = 0;
|
|
#endif // KMQUAKE2_ENGINE_MOD
|
|
|
|
// DWH: change weapon model index if necessary
|
|
if (ITEM_INDEX(ent->client->pers.weapon) == noweapon_index)
|
|
ent->s.modelindex2 = 0;
|
|
else
|
|
ent->s.modelindex2 = MAX_MODELS-1;
|
|
|
|
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[slugs_index]
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("railgun"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("railgun");
|
|
return;
|
|
}
|
|
if ( ent->client->pers.inventory[cells_index]
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("hyperblaster"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("hyperblaster");
|
|
return;
|
|
}
|
|
if ( ent->client->pers.inventory[bullets_index]
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("chaingun"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("chaingun");
|
|
return;
|
|
}
|
|
if ( ent->client->pers.inventory[bullets_index]
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("machinegun"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("machinegun");
|
|
return;
|
|
}
|
|
if ( ent->client->pers.inventory[shells_index] > 1
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("super shotgun"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("super shotgun");
|
|
return;
|
|
}
|
|
if ( ent->client->pers.inventory[shells_index]
|
|
&& ent->client->pers.inventory[ITEM_INDEX(FindItem("shotgun"))] )
|
|
{
|
|
ent->client->newweapon = FindItem ("shotgun");
|
|
return;
|
|
}
|
|
// DWH: Dude may not HAVE a blaster
|
|
//ent->client->newweapon = FindItem ("blaster");
|
|
if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("blaster"))] )
|
|
ent->client->newweapon = FindItem ("blaster");
|
|
else
|
|
ent->client->newweapon = FindItem ("No Weapon");
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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);
|
|
}
|
|
|
|
// Lazarus: Don't fire if game is frozen
|
|
if (level.freeze)
|
|
return;
|
|
|
|
if (ent->flags & FL_TURRET_OWNER)
|
|
{
|
|
// Knightmare- no weapon activity while controlling turret
|
|
ent->client->ps.gunframe = 0;
|
|
ent->client->weaponstate = WEAPON_ACTIVATING;
|
|
if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTONS_ATTACK) )
|
|
{
|
|
ent->client->latched_buttons &= ~BUTTONS_ATTACK;
|
|
turret_breach_fire (ent->turret);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
Use_Weapon
|
|
|
|
Make the weapon ready if there is ammo
|
|
================
|
|
*/
|
|
void Use_Weapon (edict_t *ent, gitem_t *in_item)
|
|
{
|
|
int ammo_index;
|
|
gitem_t *ammo_item;
|
|
int index;
|
|
gitem_t *item;
|
|
int current_weapon_index;
|
|
|
|
item = in_item;
|
|
index = ITEM_INDEX(item);
|
|
current_weapon_index = ITEM_INDEX(ent->client->pers.weapon);
|
|
|
|
// see if we're already using it
|
|
if ( (index == current_weapon_index) ||
|
|
( (index == rl_index) && (current_weapon_index == hml_index) ) ||
|
|
( (index == hml_index) && (current_weapon_index == rl_index) ) )
|
|
{
|
|
if (current_weapon_index == rl_index)
|
|
{
|
|
if (ent->client->pers.inventory[homing_index] > 0)
|
|
{
|
|
item = FindItem("homing rocket launcher");
|
|
index = hml_index;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
else if (current_weapon_index == hml_index)
|
|
{
|
|
if (ent->client->pers.inventory[rockets_index] > 0)
|
|
{
|
|
item = FindItem("rocket launcher");
|
|
index = rl_index;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (item->ammo && !g_select_empty->value && !(item->flags & IT_AMMO))
|
|
{
|
|
ammo_item = FindItem(item->ammo);
|
|
ammo_index = ITEM_INDEX(ammo_item);
|
|
|
|
if (!ent->client->pers.inventory[ammo_index])
|
|
{
|
|
// Lazarus: If player is attempting to switch to RL and doesn't have rockets,
|
|
// but DOES have homing rockets, switch to HRL
|
|
if (index == rl_index)
|
|
{
|
|
if ( (ent->client->pers.inventory[homing_index] > 0) &&
|
|
(ent->client->pers.inventory[hml_index] > 0) )
|
|
{
|
|
ent->client->newweapon = FindItem("homing rocket launcher");
|
|
return;
|
|
}
|
|
}
|
|
safe_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)
|
|
{
|
|
safe_cprintf (ent, PRINT_HIGH, "Not enough %s for %s.\n", ammo_item->pickup_name, item->pickup_name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 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) )
|
|
{
|
|
safe_cprintf (ent, PRINT_HIGH, "Can't drop current weapon\n");
|
|
return;
|
|
}
|
|
|
|
// Lazarus: Don't drop rocket launcher if current weapon is homing rocket launcher
|
|
if (index == rl_index)
|
|
{
|
|
int current_weapon_index;
|
|
current_weapon_index = ITEM_INDEX(ent->client->pers.weapon);
|
|
if (current_weapon_index == hml_index)
|
|
{
|
|
safe_cprintf (ent, PRINT_HIGH, "Can't drop current weapon\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
Drop_Item (ent, item);
|
|
ent->client->pers.inventory[index]--;
|
|
|
|
// Lazarus: if dropped weapon is RL, decrement HML inventory also
|
|
if (item->weapmodel == WEAP_ROCKETLAUNCHER)
|
|
ent->client->pers.inventory[hml_index] = 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_Generic2 (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;
|
|
int oldstate = ent->client->weaponstate;
|
|
qboolean haste_applied = false;
|
|
|
|
if (ent->deadflag || ent->s.modelindex != MAX_MODELS-1) // VWep animations screw up corpses
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ent->client->weaponstate == WEAPON_DROPPING)
|
|
{
|
|
if (ent->client->ps.gunframe == FRAME_DEACTIVATE_LAST)
|
|
{
|
|
ChangeWeapon (ent);
|
|
return;
|
|
}
|
|
else if ((FRAME_DEACTIVATE_LAST - ent->client->ps.gunframe) == 4)
|
|
{
|
|
ent->client->anim_priority = ANIM_REVERSE;
|
|
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
|
{
|
|
ent->s.frame = FRAME_crpain4+1;
|
|
ent->client->anim_end = FRAME_crpain1;
|
|
}
|
|
else
|
|
{
|
|
ent->s.frame = FRAME_pain304+1;
|
|
ent->client->anim_end = FRAME_pain301;
|
|
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if ((FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) < 4)
|
|
{
|
|
ent->client->anim_priority = ANIM_REVERSE;
|
|
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
|
{
|
|
ent->s.frame = FRAME_crpain4+1;
|
|
ent->client->anim_end = FRAME_crpain1;
|
|
}
|
|
else
|
|
{
|
|
ent->s.frame = FRAME_pain304+1;
|
|
ent->client->anim_end = FRAME_pain301;
|
|
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (ent->client->weaponstate == WEAPON_READY)
|
|
{
|
|
// Lazarus: Head off firing 2nd homer NOW, so firing animations aren't played
|
|
if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTONS_ATTACK) )
|
|
{
|
|
if (ent->client->ammo_index == homing_index)
|
|
{
|
|
if (ent->client->homing_rocket && ent->client->homing_rocket->inuse)
|
|
{
|
|
ent->client->latched_buttons &= ~BUTTONS_ATTACK;
|
|
ent->client->buttons &= ~BUTTONS_ATTACK;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Knightmare- catch alt fire commands
|
|
if ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK2)
|
|
{
|
|
int current_weapon_index = ITEM_INDEX(ent->client->pers.weapon);
|
|
|
|
if (current_weapon_index == rl_index) // homing rocket switch
|
|
{
|
|
if (ent->client->pers.inventory[homing_index] > 0)
|
|
Use_Weapon (ent, FindItem("homing rocket launcher"));
|
|
ent->client->latched_buttons &= ~BUTTONS_ATTACK;
|
|
ent->client->buttons &= ~BUTTONS_ATTACK;
|
|
return;
|
|
}
|
|
else if (current_weapon_index == hml_index)
|
|
{
|
|
if (ent->client->pers.inventory[rockets_index] > 0)
|
|
Use_Weapon (ent, FindItem("rocket launcher"));
|
|
ent->client->latched_buttons &= ~BUTTONS_ATTACK;
|
|
ent->client->buttons &= ~BUTTONS_ATTACK;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( ((ent->client->latched_buttons|ent->client->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);
|
|
}
|
|
ent->client->latched_buttons &= ~BUTTONS_ATTACK;
|
|
}
|
|
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])
|
|
{
|
|
//ZOID
|
|
if (!CTFApplyStrengthSound(ent))
|
|
//ZOID
|
|
if (ent->client->quad_framenum > level.framenum)
|
|
gi.sound (ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
|
|
|
|
//ZOID
|
|
CTFApplyHasteSound(ent);
|
|
//ZOID
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
//ZOID
|
|
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 oldstate = ent->client->weaponstate;
|
|
|
|
Weapon_Generic2 (ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST,
|
|
FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames,
|
|
fire_frames, fire);
|
|
|
|
// run the weapon frame again if hasted
|
|
if (Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0 &&
|
|
ent->client->weaponstate == WEAPON_FIRING)
|
|
return;
|
|
|
|
if ((CTFApplyHaste(ent) ||
|
|
(Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0 &&
|
|
ent->client->weaponstate != WEAPON_FIRING))
|
|
&& oldstate == ent->client->weaponstate)
|
|
{
|
|
Weapon_Generic2 (ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST,
|
|
FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames,
|
|
fire_frames, fire);
|
|
}
|
|
}
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
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 = sk_hand_grenade_radius->value; // was damage + 40
|
|
radius = 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;
|
|
|
|
if (ent->deadflag || ent->s.modelindex != MAX_MODELS-1) // VWep animations screw up corpses
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ent->health <= 0)
|
|
return;
|
|
|
|
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
|
{
|
|
ent->client->anim_priority = ANIM_ATTACK;
|
|
ent->s.frame = FRAME_crattak1-1;
|
|
ent->client->anim_end = FRAME_crattak3;
|
|
}
|
|
else
|
|
{
|
|
ent->client->anim_priority = ANIM_REVERSE;
|
|
ent->s.frame = FRAME_wave08;
|
|
ent->client->anim_end = FRAME_wave01;
|
|
}
|
|
}
|
|
|
|
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 = (int)sk_grenade_damage->value;
|
|
float radius;
|
|
|
|
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);
|
|
// temporary crap to test grenade bounce
|
|
// fire_grenade (ent, start, forward, damage, (int)sk_grenade_speed->value, 25, radius);
|
|
|
|
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]--;
|
|
}
|
|
|
|
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
|
|
|
|
======================================================================
|
|
*/
|
|
edict_t *rocket_target (edict_t *self, vec3_t start, vec3_t forward)
|
|
{
|
|
float bd, d;
|
|
int i;
|
|
edict_t *who, *best;
|
|
trace_t tr;
|
|
vec3_t dir, end;
|
|
|
|
VectorMA(start, WORLD_SIZE, forward, end); // was 8192
|
|
|
|
/* Check for aiming directly at a damageable entity */
|
|
tr = gi.trace(start, NULL, NULL, end, self, MASK_SHOT);
|
|
if ((tr.ent->takedamage != DAMAGE_NO) && (tr.ent->solid != SOLID_NOT))
|
|
return tr.ent;
|
|
|
|
/* Check for damageable entity within a tolerance of view angle */
|
|
bd = 0;
|
|
best = NULL;
|
|
for (i=1, who=g_edicts+1; i<globals.num_edicts; i++, who++) {
|
|
if (!who->inuse)
|
|
continue;
|
|
if (who == self)
|
|
continue;
|
|
if (who->takedamage == DAMAGE_NO)
|
|
continue;
|
|
if (who->solid == SOLID_NOT)
|
|
continue;
|
|
VectorMA(who->absmin,0.5,who->size,end);
|
|
tr = gi.trace (start, vec3_origin, vec3_origin, end, self, MASK_OPAQUE);
|
|
if (tr.fraction < 1.0)
|
|
continue;
|
|
VectorSubtract(end, self->s.origin, dir);
|
|
VectorNormalize(dir);
|
|
d = DotProduct(forward, dir);
|
|
if (d > bd) {
|
|
bd = d;
|
|
best = who;
|
|
}
|
|
}
|
|
if (bd > 0.90)
|
|
return best;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
// Knightmare- changed constant 650 for cvar sk_rocket_speed->value
|
|
if (ent->client->pers.fire_mode)
|
|
{
|
|
edict_t *target;
|
|
|
|
if (ent->client->homing_rocket && ent->client->homing_rocket->inuse)
|
|
{
|
|
ent->client->ps.gunframe++;
|
|
return;
|
|
}
|
|
|
|
target = rocket_target(ent, start, forward);
|
|
fire_rocket (ent, start, forward, damage, (int)sk_rocket_speed->value, damage_radius, radius_damage, target);
|
|
}
|
|
else
|
|
fire_rocket (ent, start, forward, damage, (int)sk_rocket_speed->value, damage_radius, radius_damage, NULL);
|
|
|
|
// 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);
|
|
|
|
ent->client->ps.gunframe++;
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void Weapon_HomingMissileLauncher_Fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
ent->client->pers.fire_mode = 1;
|
|
Weapon_RocketLauncher_Fire (ent, false);
|
|
ent->client->pers.fire_mode = 0;
|
|
}
|
|
|
|
void Weapon_HomingMissileLauncher (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_HomingMissileLauncher_Fire);
|
|
}
|
|
/*
|
|
======================================================================
|
|
|
|
BLASTER / HYPERBLASTER
|
|
|
|
======================================================================
|
|
*/
|
|
|
|
void 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;
|
|
|
|
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 (!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);
|
|
|
|
// 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
|
|
// Knightmare- different flash if in chasecam mode
|
|
if (ent->client && ent->client->chasetoggle)
|
|
{
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (muzzleflash | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
}
|
|
else
|
|
{
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (muzzleflash | is_silenced);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
}
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
}
|
|
|
|
|
|
void Weapon_Blaster_Fire (edict_t *ent, qboolean altfire)
|
|
{
|
|
int damage;
|
|
int effect;
|
|
int color;
|
|
|
|
if (deathmatch->value)
|
|
damage = (int)sk_blaster_damage_dm->value;
|
|
else
|
|
damage = (int)sk_blaster_damage->value;
|
|
|
|
// selectable 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;
|
|
// CTF color override
|
|
if ( (int)ctf->value && (int)ctf_blastercolors->value && ent->client )
|
|
color = (5 - ent->client->resp.ctf_team);
|
|
#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);
|
|
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;
|
|
int damage;
|
|
int 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- selectable 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;
|
|
// CTF color override
|
|
if ( (int)ctf->value && (int)ctf_blastercolors->value && ent->client )
|
|
color = 5-ent->client->resp.ctf_team;
|
|
#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;
|
|
Blaster_Fire (ent, offset, damage, true, effect, color);
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
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]--;
|
|
|
|
ent->client->anim_priority = ANIM_ATTACK;
|
|
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
|
{
|
|
ent->s.frame = FRAME_crattak1 - (int) (random()+0.25);
|
|
ent->client->anim_end = FRAME_crattak9;
|
|
}
|
|
else
|
|
{
|
|
ent->s.frame = FRAME_attack1 - (int) (random()+0.25);
|
|
ent->client->anim_end = FRAME_attack8;
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
ent->client->anim_priority = ANIM_ATTACK;
|
|
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
|
{
|
|
ent->s.frame = FRAME_crattak1 - (ent->client->ps.gunframe & 1);
|
|
ent->client->anim_end = FRAME_crattak9;
|
|
}
|
|
else
|
|
{
|
|
ent->s.frame = FRAME_attack1 - (ent->client->ps.gunframe & 1);
|
|
ent->client->anim_end = FRAME_attack8;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index] -= shots;
|
|
}
|
|
|
|
|
|
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]--;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// CTF color override
|
|
if ( (int)ctf->value && (int)ctf_railcolors->value && ent->client )
|
|
{
|
|
useColor = true;
|
|
if (ent->client->resp.ctf_team == CTF_TEAM1) {
|
|
red = 176; green = 20; blue = 20;
|
|
}
|
|
else if (ent->client->resp.ctf_team == CTF_TEAM2) {
|
|
red = 20; green = 20; blue = 176;
|
|
}
|
|
else if (ent->client->resp.ctf_team == CTF_TEAM3) {
|
|
red = 20; green = 176; blue = 20;
|
|
}
|
|
}
|
|
// custom client color
|
|
else 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);
|
|
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);
|
|
|
|
ent->client->ps.gunframe++;
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
|
|
|
VectorScale (forward, -2, ent->client->kick_origin);
|
|
|
|
// 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;
|
|
|
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
fire_bfg (ent, start, forward, damage, (int)sk_bfg_speed->value, damage_radius);
|
|
|
|
ent->client->ps.gunframe++;
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
|
ent->client->pers.inventory[ent->client->ammo_index] -= 50;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
//======================================================================
|
|
void Weapon_Null(edict_t *ent)
|
|
{
|
|
if (ent->client->newweapon)
|
|
ChangeWeapon(ent);
|
|
}
|
|
//======================================================================
|
|
qboolean Pickup_Health (edict_t *ent, edict_t *other);
|
|
void kick_attack (edict_t *ent)
|
|
{
|
|
vec3_t start;
|
|
vec3_t forward, right;
|
|
vec3_t offset;
|
|
int damage = (int)sk_jump_kick_damage->value;
|
|
int kick = 300;
|
|
trace_t tr;
|
|
vec3_t end;
|
|
|
|
if (ent->client->quad_framenum > level.framenum)
|
|
{
|
|
damage *= 4;
|
|
kick *= 4;
|
|
}
|
|
|
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
|
|
|
VectorScale (forward, 0, ent->client->kick_origin);
|
|
|
|
VectorSet(offset, 0, 0, ent->viewheight-20);
|
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
|
|
|
VectorMA( start, 25, forward, end );
|
|
|
|
tr = gi.trace (ent->s.origin, NULL, NULL, end, ent, MASK_SHOT);
|
|
|
|
// don't need to check for water
|
|
if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
|
|
{
|
|
if (tr.fraction < 1.0)
|
|
{
|
|
if (tr.ent->takedamage)
|
|
{
|
|
if ( tr.ent->health <= 0 )
|
|
return;
|
|
//Knightmare- don't jump kick exploboxes or pushable crates, or insanes, or ambient models
|
|
if (!strcmp(tr.ent->classname, "misc_explobox") || !strcmp(tr.ent->classname, "func_pushable")
|
|
|| !strcmp(tr.ent->classname, "model_spawn") || !strcmp(tr.ent->classname, "model_train")
|
|
|| !strcmp(tr.ent->classname, "misc_insane"))
|
|
return;
|
|
//also don't jumpkick actors, unless they're bad guys
|
|
if (!strcmp(tr.ent->classname, "misc_actor") && (tr.ent->monsterinfo.aiflags & AI_GOOD_GUY))
|
|
return;
|
|
//nor goodguy monsters
|
|
if (strstr(tr.ent->classname, "monster_") && tr.ent->monsterinfo.aiflags & AI_GOOD_GUY)
|
|
return;
|
|
//nor shootable items
|
|
if (tr.ent->item && !strstr(tr.ent->classname, "monster_") && (strstr(tr.ent->classname, "ammo_") || strstr(tr.ent->classname, "weapon_")
|
|
|| strstr(tr.ent->classname, "item_") || strstr(tr.ent->classname, "key_") || tr.ent->item->pickup == Pickup_Health) )
|
|
return;
|
|
if (((tr.ent != ent) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value) && OnSameTeam (tr.ent, ent)))
|
|
return;
|
|
// zucc stop powerful upwards kicking
|
|
//forward[2] = 0;
|
|
|
|
// glass fx
|
|
T_Damage (tr.ent, ent, ent, forward, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_KICK );
|
|
gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/kick.wav"), 1, ATTN_NORM, 0);
|
|
PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
|
|
ent->client->jumping = 0; // only 1 jumpkick per jump
|
|
}
|
|
}
|
|
}
|
|
}
|