thirtyflightsofloving/game/p_weapon.c
Knightmare66 95295401a4 Added support for custom client railgun colors in 3ZB2, Awakening2, and Zaero DLLs.
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.
2021-10-31 03:53:09 -04:00

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
}
}
}
}