openmohaa/code/fgame/weapon.cpp
2023-06-15 23:44:02 +02:00

4602 lines
88 KiB
C++

/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA 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.
OpenMoHAA 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// weapon.cpp: Source file for Weapon class. The weapon class is the base class for
// all weapons in Sin. Any entity created from a class derived from the weapon
// class will be usable by any Sentient (players and monsters) as a weapon.
//
#include "g_local.h"
#include "g_phys.h"
#include "entity.h"
#include "item.h"
#include "weapon.h"
#include "scriptmaster.h"
#include "sentient.h"
#include "misc.h"
#include "specialfx.h"
#include "actor.h"
#include "weaputils.h"
#include "player.h"
#include "vehicleturret.h"
#include "debuglines.h"
Event EV_Weapon_Shoot
(
"shoot",
EV_DEFAULT,
"S",
"mode",
"Shoot the weapon",
EV_NORMAL
);
Event EV_Weapon_DoneRaising
(
"ready",
EV_DEFAULT,
NULL,
NULL,
"Signals the end of the ready animation so the weapon can be used",
EV_NORMAL
);
Event EV_Weapon_DoneFiring
(
"donefiring",
EV_DEFAULT,
NULL,
NULL,
"Signals the end of the fire animation",
EV_NORMAL
);
Event EV_Weapon_Idle
(
"idle",
EV_DEFAULT,
NULL,
NULL,
"Puts the weapon into an idle state",
EV_NORMAL
);
Event EV_Weapon_IdleInit
(
"idleinit",
EV_DEFAULT,
NULL,
NULL,
"Puts the weapon into an idle state and clears all the anim slots",
EV_NORMAL
);
Event EV_Weapon_SecondaryUse
(
"secondaryuse",
EV_DEFAULT,
NULL,
NULL,
"Puts the weapon into its secondary mode of operation",
EV_NORMAL
);
Event EV_Weapon_DoneReloading
(
"donereloading",
EV_DEFAULT,
NULL,
NULL,
"Signals the end of the reload animation",
EV_NORMAL
);
Event EV_Weapon_SetAmmoClipSize
(
"clipsize",
EV_DEFAULT,
"i",
"ammoClipSize",
"Set the amount of rounds a clip of the weapon holds",
EV_NORMAL
);
Event EV_Weapon_SetAmmoInClip
(
"ammo_in_clip",
EV_DEFAULT,
"i",
"ammoInClip",
"Set the amount of ammo in the clip",
EV_NORMAL
);
Event EV_Weapon_SetShareClip
(
"shareclip",
EV_DEFAULT,
"i",
"shareClip",
"Sets the weapon to share the same clip between all fire modes",
EV_NORMAL
);
Event EV_Weapon_FillClip
(
"clip_fill",
EV_DEFAULT,
NULL,
NULL,
"Fills the weapons ammo clip with ammo from its owner",
EV_NORMAL
);
Event EV_Weapon_EmptyClip
(
"clip_empty",
EV_DEFAULT,
NULL,
NULL,
"Empties the weapon's clip of ammo, returning it to the owner",
EV_NORMAL
);
Event EV_Weapon_AddClip
(
"clip_add",
EV_DEFAULT,
"i",
"ammoAmount",
"Add to the weapons ammo clip with ammo from its owner",
EV_NORMAL
);
Event EV_Weapon_SetMaxRange
(
"maxrange",
EV_DEFAULT,
"f",
"maxRange",
"Set the maximum range of a weapon so the AI knows how to use it",
EV_NORMAL
);
Event EV_Weapon_SetMinRange
(
"minrange",
EV_DEFAULT,
"f",
"minRange",
"Set the minimum range of a weapon so the AI knows how to use it",
EV_NORMAL
);
Event EV_Weapon_SetFireDelay
(
"firedelay",
EV_DEFAULT,
"f",
"fFireDelay",
"Set the minimum time between shots from the weapon",
EV_NORMAL
);
Event EV_Weapon_DMSetFireDelay
(
"dmfiredelay",
EV_DEFAULT,
"f",
"fFireDelay",
"Set the minimum time between shots from the weapon",
EV_NORMAL
);
Event EV_Weapon_NotDroppable
(
"notdroppable",
EV_DEFAULT,
NULL,
NULL,
"Makes a weapon not droppable"
);
Event EV_Weapon_SetAimAnim
(
"setaimanim",
EV_DEFAULT,
"si",
"aimAnimation aimFrame",
"Set the aim animation and frame for when a weapon fires",
EV_NORMAL
);
Event EV_Weapon_SetRank
(
"rank",
EV_DEFAULT,
"ii",
"iOrder iRank",
"Set the order value and power ranking for the weapon"
);
Event EV_Weapon_SetFireType
(
"firetype",
EV_DEFAULT,
"s",
"firingType",
"Set the firing type of the weapon (projectile or bullet)",
EV_NORMAL
);
Event EV_Weapon_SetAIRange
(
"airange",
EV_DEFAULT,
"s",
"airange",
"Set the range of this gun for the ai: short, medium, long, sniper",
EV_NORMAL
);
Event EV_Weapon_SetProjectile
(
"projectile",
EV_DEFAULT,
"s",
"projectileModel",
"Set the model of the projectile that this weapon fires",
EV_NORMAL
);
Event EV_Weapon_SetDMProjectile
(
"dmprojectile",
EV_DEFAULT,
"s",
"projectileModel",
"Set the model of the projectile that this weapon fires",
EV_NORMAL
);
Event EV_Weapon_SetBulletDamage
(
"bulletdamage",
EV_DEFAULT,
"f",
"bulletDamage",
"Set the damage that the bullet causes",
EV_NORMAL
);
Event EV_Weapon_SetDMBulletDamage
(
"dmbulletdamage",
EV_DEFAULT,
"f",
"bulletDamage",
"Set the damage that the bullet causes",
EV_NORMAL
);
Event EV_Weapon_SetBulletKnockback
(
"bulletknockback",
EV_DEFAULT,
"f",
"bulletKnockback",
"Set the knockback that the bullet causes",
EV_NORMAL
);
Event EV_Weapon_SetDMBulletKnockback
(
"dmbulletknockback",
EV_DEFAULT,
"f",
"bulletKnockback",
"Set the knockback that the bullet causes",
EV_NORMAL
);
Event EV_Weapon_SetBulletCount
(
"bulletcount",
EV_DEFAULT,
"f",
"bulletCount",
"Set the number of bullets this weapon shoots when fired",
EV_NORMAL
);
Event EV_Weapon_SetDMBulletCount
(
"dmbulletcount",
EV_DEFAULT,
"f",
"bulletCount",
"Set the number of bullets this weapon shoots when fired",
EV_NORMAL
);
Event EV_Weapon_SetBulletRange
(
"bulletrange",
EV_DEFAULT,
"f",
"bulletRange",
"Set the range of the bullets",
EV_NORMAL
);
Event EV_Weapon_SetDMBulletRange
(
"dmbulletrange",
EV_DEFAULT,
"f",
"bulletRange",
"Set the range of the bullets",
EV_NORMAL
);
Event EV_Weapon_SetBulletSpread
(
"bulletspread",
EV_DEFAULT,
"ffFF",
"bulletSpreadX bulletSpreadY bulletSpreadXmax bulletSpreadYmax",
"Set the min & optional max spread of the bullet in the x and y axis",
EV_NORMAL
);
Event EV_Weapon_SetDMBulletSpread
(
"dmbulletspread",
EV_DEFAULT,
"ffFF",
"bulletSpreadX bulletSpreadY bulletSpreadXmax bulletSpreadYmax",
"Set the min & optional max spread of the bullet in the x and y axis",
EV_NORMAL
);
Event EV_Weapon_SetZoomSpreadMult
(
"zoomspreadmult",
EV_DEFAULT,
"f",
"scale",
"Sets the spread multiplier for when using the zoom on a zooming weapon",
EV_NORMAL
);
Event EV_Weapon_SetDMZoomSpreadMult
(
"dmzoomspreadmult",
EV_DEFAULT,
"f",
"scale",
"Sets the spread multiplier for when using the zoom on a zooming weapon",
EV_NORMAL
);
Event EV_Weapon_SetFireSpreadMult
(
"firespreadmult",
EV_DEFAULT,
"ffff",
"scaleadd falloff cap maxtime",
"Sets a time decayed multiplyer to spread when the weapon is fired",
EV_NORMAL
);
Event EV_Weapon_SetDMFireSpreadMult
(
"dmfirespreadmult",
EV_DEFAULT,
"ffff",
"scaleadd falloff cap maxtime",
"Sets a time decayed multiplyer to spread when the weapon is fired",
EV_NORMAL
);
Event EV_Weapon_SetTracerFrequency
(
"tracerfrequency",
EV_DEFAULT,
"f",
"frequenct",
"Set the frequency of making tracers"
);
Event EV_Weapon_SetRange
(
"range",
EV_DEFAULT,
"f",
"range",
"Set the range of the weapon",
EV_NORMAL
);
Event EV_Weapon_Secondary
(
"secondary",
EV_DEFAULT,
"SSSSSSSS",
"arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8",
"Set the secondary mode of the weapon, by passing commands through",
EV_NORMAL
);
Event EV_Weapon_AmmoType
(
"ammotype",
EV_DEFAULT,
"s",
"name",
"Set the type of ammo this weapon uses",
EV_NORMAL
);
Event EV_Weapon_StartAmmo
(
"startammo",
EV_DEFAULT,
"i",
"amount",
"Set the starting ammo of this weapon",
EV_NORMAL
);
Event EV_Weapon_DMStartAmmo
(
"dmstartammo",
EV_DEFAULT,
"i",
"amount",
"Set the starting ammo of this weapon",
EV_NORMAL
);
Event EV_Weapon_AmmoRequired
(
"ammorequired",
EV_DEFAULT,
"i",
"amount",
"Set the amount of ammo this weapon requires to fire",
EV_NORMAL
);
Event EV_Weapon_DMAmmoRequired
(
"dmammorequired",
EV_DEFAULT,
"i",
"amount",
"Set the amount of ammo this weapon requires to fire",
EV_NORMAL
);
Event EV_Weapon_MaxChargeTime
(
"maxchargetime",
EV_DEFAULT,
"i",
"time",
"Set the maximum time the weapon may be charged up",
EV_NORMAL
);
Event EV_Weapon_MinChargeTime
(
"minchargetime",
EV_DEFAULT,
"i",
"time",
"Set the minimum time the weapon must be charged up",
EV_NORMAL
);
Event EV_Weapon_GiveStartingAmmo
(
"startingammotoowner",
EV_DEFAULT,
NULL,
NULL,
"Internal event used to give ammo to the owner of the weapon",
EV_NORMAL
);
Event EV_Weapon_AutoAim
(
"autoaim",
EV_DEFAULT,
NULL,
NULL,
"Turn on auto aiming for the weapon",
EV_NORMAL
);
Event EV_Weapon_Crosshair
(
"crosshair",
EV_DEFAULT,
"b",
"bool",
"Turn on/off the crosshair for this weapon",
EV_NORMAL
);
Event EV_Weapon_DMCrosshair
(
"dmcrosshair",
EV_DEFAULT,
"b",
"bool",
"Turn on/off the crosshair for this weapon",
EV_NORMAL
);
Event EV_Weapon_SetQuiet
(
"quiet",
EV_DEFAULT,
NULL,
NULL,
"Makes the weapon make no noise.",
EV_NORMAL
);
Event EV_Weapon_SetLoopFire
(
"loopfire",
EV_DEFAULT,
NULL,
NULL,
"Makes the weapon fire by looping the fire animation.",
EV_NORMAL
);
Event EV_Weapon_OffHandAttachToTag
(
"offhandattachtotag",
EV_DEFAULT,
"s",
"tagname",
"Set the name of the tag to attach this to it's owner's off hand.",
EV_NORMAL
);
Event EV_Weapon_MainAttachToTag
(
"mainattachtotag",
EV_DEFAULT,
"s",
"tagname",
"Set the name of the tag to attach this to it's owner when being used.",
EV_NORMAL
);
Event EV_Weapon_HolsterTag
(
"holstertag",
EV_DEFAULT,
"s",
"tagname",
"Set the name of the tag to attach this to when the weapon is holstered.",
EV_NORMAL
);
Event EV_Weapon_HolsterOffset
(
"holsteroffset",
EV_DEFAULT,
"v",
"offset",
"Set the positional offset when it is holstered",
EV_NORMAL
);
Event EV_Weapon_HolsterAngles
(
"holsterangles",
EV_DEFAULT,
"v",
"angles",
"Set the angles of this weapon when it is holstered",
EV_NORMAL
);
Event EV_Weapon_HolsterScale
(
"holsterscale",
EV_DEFAULT,
"f",
"scale",
"Set the scale of this weapon when it is holstered",
EV_NORMAL
);
Event EV_Weapon_AutoPutaway
(
"autoputaway",
EV_DEFAULT,
"b",
"bool",
"Set the weapon to be automatically put away when out of ammo",
EV_NORMAL
);
Event EV_Weapon_UseNoAmmo
(
"usenoammo",
EV_DEFAULT,
"b",
"bool",
"Set the weapon to be able to be used when it's out of ammo",
EV_NORMAL
);
Event EV_Weapon_SetMeansOfDeath
(
"meansofdeath",
EV_DEFAULT,
"s",
"meansOfDeath",
"Set the meansOfDeath of the weapon.",
EV_NORMAL
);
Event EV_Weapon_SetWorldHitSpawn
(
"worldhitspawn",
EV_DEFAULT,
"s",
"modelname",
"Set a model to be spawned when the weapon strikes the world->",
EV_NORMAL
);
Event EV_Weapon_MakeNoise
(
"makenoise",
EV_DEFAULT,
"FB",
"noise_radius force",
"Makes the weapon make noise that actors can hear.",
EV_NORMAL
);
Event EV_Weapon_SetWeaponType
(
"weapontype",
EV_DEFAULT,
"s",
"weapon_type",
"Sets the weapon type",
EV_NORMAL
);
Event EV_Weapon_SetWeaponGroup
(
"weapongroup",
EV_DEFAULT,
"s",
"weapon_group",
"Sets the weapon group, a set of animations for actor animations scripts to use",
EV_NORMAL
);
Event EV_Weapon_SetZoom
(
"zoom",
EV_DEFAULT,
"iI",
"zoomfov autozoom",
"Sets fov to zoom to on a secondary fire"
);
Event EV_Weapon_SetSemiAuto
(
"semiauto",
EV_DEFAULT,
NULL,
NULL,
"Sets the weapon to fire semi-auto"
);
Event EV_Weapon_AttachToHand
(
"attachtohand",
EV_DEFAULT,
"s",
"weapon_hand",
"Attaches an active weapon to the specified hand"
);
Event EV_Weapon_CantPartialReload
(
"cantpartialreload",
EV_DEFAULT,
NULL,
NULL,
"Prevents the weapon from being reloaded part way through a clip"
);
Event EV_Weapon_DMCantPartialReload
(
"dmcantpartialreload",
EV_DEFAULT,
NULL,
NULL,
"Prevents the weapon from being reloaded part way through a clip for DM"
);
Event EV_Weapon_FallingAngleAdjust
(
"fallingangleadjust",
EV_DEFAULT,
0,
0,
"Adjusts the weapons angles as it falls to the ground"
);
Event EV_Weapon_SetViewKick
(
"viewkick",
EV_DEFAULT,
"ffFF",
"pitchmin pitchmax yawmin yawmax",
"Adds kick to the view of the owner when fired."
);
Event EV_Weapon_MovementSpeed
(
"movementspeed",
EV_DEFAULT,
"f",
"speedmult",
"Alters the movement speed of the player when he has the weapon out"
);
Event EV_Weapon_DMMovementSpeed
(
"dmmovementspeed",
EV_DEFAULT,
"f",
"speedmult",
"Alters the movement speed of the player when he has the weapon out"
);
Event EV_Weapon_AmmoPickupSound
(
"ammopickupsound",
EV_DEFAULT,
"s",
"name",
"sets the weapon's ammo pickup sound alias"
);
Event EV_Weapon_NoAmmoSound
(
"noammosound",
EV_DEFAULT,
"s",
"name",
"sets the weapon's dry fire sound alias"
);
CLASS_DECLARATION( Item, Weapon, NULL )
{
{ &EV_SetAnim, &Weapon::SetWeaponAnimEvent },
{ &EV_Item_Pickup, &Weapon::PickupWeapon },
{ &EV_Weapon_DoneRaising, &Weapon::DoneRaising },
{ &EV_Weapon_DoneFiring, &Weapon::DoneFiring },
{ &EV_Weapon_Idle, &Weapon::Idle },
{ &EV_Weapon_IdleInit, &Weapon::IdleInit },
{ &EV_BroadcastAIEvent, &Weapon::WeaponSound },
{ &EV_Weapon_DoneReloading, &Weapon::DoneReloading },
{ &EV_Weapon_SetAmmoClipSize, &Weapon::SetAmmoClipSize },
{ &EV_Weapon_SetAmmoInClip, &Weapon::SetAmmoInClip },
{ &EV_Weapon_SetShareClip, &Weapon::SetShareClip },
{ &EV_Weapon_FillClip, &Weapon::EventClipFill },
{ &EV_Weapon_EmptyClip, &Weapon::EventClipEmpty },
{ &EV_Weapon_AddClip, &Weapon::EventClipAdd },
{ &EV_Weapon_SetMaxRange, &Weapon::SetMaxRangeEvent },
{ &EV_Weapon_SetMinRange, &Weapon::SetMinRangeEvent },
{ &EV_Weapon_SetFireDelay, &Weapon::EventSetFireDelay },
{ &EV_Weapon_NotDroppable, &Weapon::NotDroppableEvent },
{ &EV_Weapon_SetAimAnim, &Weapon::SetAimAnim },
{ &EV_Weapon_Shoot, &Weapon::Shoot },
{ &EV_Weapon_SetRank, &Weapon::SetRankEvent },
{ &EV_Weapon_SetFireType, &Weapon::SetFireType },
{ &EV_Weapon_SetAIRange, &Weapon::SetAIRange },
{ &EV_Weapon_SetProjectile, &Weapon::SetProjectile },
{ &EV_Weapon_SetDMProjectile, &Weapon::SetDMProjectile },
{ &EV_Weapon_SetBulletDamage, &Weapon::SetBulletDamage },
{ &EV_Weapon_SetBulletCount, &Weapon::SetBulletCount },
{ &EV_Weapon_SetBulletKnockback, &Weapon::SetBulletKnockback },
{ &EV_Weapon_SetBulletRange, &Weapon::SetBulletRange },
{ &EV_Weapon_SetRange, &Weapon::SetRange },
{ &EV_Weapon_SetBulletSpread, &Weapon::SetBulletSpread },
{ &EV_Weapon_SetTracerFrequency, &Weapon::SetTracerFrequency },
{ &EV_Weapon_Secondary, &Weapon::Secondary },
{ &EV_Weapon_AmmoType, &Weapon::SetAmmoType },
{ &EV_Weapon_StartAmmo, &Weapon::SetStartAmmo },
{ &EV_Weapon_AmmoRequired, &Weapon::SetAmmoRequired },
{ &EV_Weapon_MaxChargeTime, &Weapon::SetMaxChargeTime },
{ &EV_Weapon_MinChargeTime, &Weapon::SetMinChargeTime },
{ &EV_Weapon_GiveStartingAmmo, &Weapon::GiveStartingAmmoToOwner },
{ &EV_Weapon_AutoAim, &Weapon::AutoAim },
{ &EV_Weapon_Crosshair, &Weapon::Crosshair },
{ &EV_Weapon_MainAttachToTag, &Weapon::MainAttachToTag },
{ &EV_Weapon_OffHandAttachToTag, &Weapon::OffHandAttachToTag },
{ &EV_Weapon_HolsterTag, &Weapon::HolsterAttachToTag },
{ &EV_Weapon_HolsterOffset, &Weapon::SetHolsterOffset },
{ &EV_Weapon_HolsterAngles, &Weapon::SetHolsterAngles },
{ &EV_Weapon_HolsterScale, &Weapon::SetHolsterScale },
{ &EV_Weapon_SetQuiet, &Weapon::SetQuiet },
{ &EV_Weapon_SetLoopFire, &Weapon::SetLoopFire },
{ &EV_Weapon_AutoPutaway, &Weapon::SetAutoPutaway },
{ &EV_Weapon_UseNoAmmo, &Weapon::SetUseNoAmmo },
{ &EV_Weapon_SetMeansOfDeath, &Weapon::SetMeansOfDeath },
{ &EV_Weapon_SetWorldHitSpawn, &Weapon::SetWorldHitSpawn },
{ &EV_Weapon_MakeNoise, &Weapon::MakeNoise },
{ &EV_Weapon_SetWeaponType, &Weapon::SetWeaponType },
{ &EV_Weapon_SetWeaponGroup, &Weapon::SetWeaponGroup },
{ &EV_Weapon_SetZoom, &Weapon::SetZoom },
{ &EV_Weapon_SetSemiAuto, &Weapon::SetSemiAuto },
{ &EV_Weapon_AttachToHand, &Weapon::AttachToHand },
{ &EV_Weapon_CantPartialReload, &Weapon::SetCantPartialReload },
{ &EV_Weapon_FallingAngleAdjust, &Weapon::FallingAngleAdjust },
{ &EV_Weapon_SetViewKick, &Weapon::SetViewKick },
{ &EV_Weapon_DMSetFireDelay, &Weapon::EventSetDMFireDelay },
{ &EV_Weapon_SetDMBulletDamage, &Weapon::SetDMBulletDamage },
{ &EV_Weapon_SetDMBulletCount, &Weapon::SetDMBulletCount },
{ &EV_Weapon_SetDMBulletSpread, &Weapon::SetDMBulletSpread },
{ &EV_Weapon_SetZoomSpreadMult, &Weapon::SetZoomSpreadMult },
{ &EV_Weapon_SetDMZoomSpreadMult, &Weapon::SetDMZoomSpreadMult },
{ &EV_Weapon_SetFireSpreadMult, &Weapon::SetFireSpreadMult },
{ &EV_Weapon_SetDMFireSpreadMult, &Weapon::SetDMFireSpreadMult },
{ &EV_Weapon_DMAmmoRequired, &Weapon::SetDMAmmoRequired },
{ &EV_Weapon_DMCantPartialReload, &Weapon::SetDMCantPartialReload },
{ &EV_Weapon_DMStartAmmo, &Weapon::SetDMStartAmmo },
{ &EV_Weapon_SetDMBulletRange, &Weapon::SetDMBulletRange },
{ &EV_Weapon_DMCrosshair, &Weapon::DMCrosshair },
{ &EV_Weapon_MovementSpeed, &Weapon::SetMovementSpeed },
{ &EV_Weapon_DMMovementSpeed, &Weapon::SetDMMovementSpeed },
{ &EV_Weapon_AmmoPickupSound, &Weapon::EventAmmoPickupSound },
{ &EV_Weapon_NoAmmoSound, &Weapon::EventNoAmmoSound },
{ NULL, NULL }
};
//======================
//Weapon::Weapon
//======================
Weapon::Weapon()
{
entflags |= EF_WEAPON;
mAIRange = RANGE_SHORT;
if( LoadingSavegame )
{
// Archive function will setup all necessary data
return;
}
// Owner of the weapon
owner = NULL;
// Starting rank of the weapon
rank = 0;
// Amount of ammo required for weapon
INITIALIZE_WEAPONMODE_VAR( ammorequired, 0 );
// Starting ammo of the weapon
INITIALIZE_WEAPONMODE_VAR( startammo, 0 );
// Amount of ammo the clip can hold
INITIALIZE_WEAPONMODE_VAR( ammo_clip_size, 0 );
// Amount of ammo in clip
INITIALIZE_WEAPONMODE_VAR( ammo_in_clip, 0 );
// Amount of time to pass before broadcasting a weapon sound again
nextweaponsoundtime = 0;
// The initial state of the weapon
weaponstate = WEAPON_HOLSTERED;
// Is the weapon droppable when the owner is killed
notdroppable = false;
// Aim animation for behavior of monsters
aimanim = -1;
aimframe = 0;
m_iAnimSlot = 0;
// start off unattached
attached = false;
// maximum effective firing distance (for autoaim)
maxrange = 1000;
// minimum safe firing distance (for AI)
minrange = 0;
// speed of the projectile (0 == infinite speed)
memset( projectilespeed, 0, sizeof( projectilespeed ) );
// default action_level_increment
INITIALIZE_WEAPONMODE_VAR( action_level_increment, 2 );
// Weapons don't move
setMoveType( MOVETYPE_NONE );
m_fLastFireTime = 0;
// What type of ammo this weapon fires
INITIALIZE_WEAPONMODE_VAR( firetype, ( firetype_t )0 );
INITIALIZE_WEAPONMODE_VAR( firedelay, 0.1f );
// Init the bullet specs
INITIALIZE_WEAPONMODE_VAR( projectilespeed, 0 );
INITIALIZE_WEAPONMODE_VAR( bulletdamage, 0 );
INITIALIZE_WEAPONMODE_VAR( bulletcount, 1 );
INITIALIZE_WEAPONMODE_VAR( bulletrange, 1024 );
INITIALIZE_WEAPONMODE_VAR( bulletknockback, 0 );
INITIALIZE_WEAPONMODE_VAR( ammo_type, "" );
INITIALIZE_WEAPONMODE_VAR( loopfire, false );
INITIALIZE_WEAPONMODE_VAR( quiet, qfalse );
INITIALIZE_WEAPONMODE_VAR( loopfire, qfalse );
INITIALIZE_WEAPONMODE_VAR( tracercount, 0 );
INITIALIZE_WEAPONMODE_VAR( tracerfrequency, 0 );
for( int i = 0; i < MAX_FIREMODES; i++ )
{
INITIALIZE_WEAPONMODE_VAR( viewkickmin[ i ], 0 );
INITIALIZE_WEAPONMODE_VAR( viewkickmax[ i ], 0 );
}
INITIALIZE_WEAPONMODE_VAR( m_fFireSpreadMultAmount, 0 );
INITIALIZE_WEAPONMODE_VAR( m_fFireSpreadMultFalloff, 0 );
INITIALIZE_WEAPONMODE_VAR( m_fFireSpreadMultCap, 0 );
INITIALIZE_WEAPONMODE_VAR( m_fFireSpreadMultTime, 0 );
INITIALIZE_WEAPONMODE_VAR( m_fFireSpreadMult, 0 );
// Init the max amount of time a weapon may be charged (5 seconds)
INITIALIZE_WEAPONMODE_VAR( min_charge_time, 0 );
INITIALIZE_WEAPONMODE_VAR( max_charge_time, 5 );
charge_fraction = 1.0f;
// Tag to attach this weapon to on its owner when used in the left hand and in the right hand
attachToTag_offhand = "tag_weapon_left";
attachToTag_main = "tag_weapon_right";
// putaway is flagged true when the weapon should be put away by state machine
putaway = false;
// This is used for setting alternate fire functionality when initializing stuff
firemodeindex = FIRE_PRIMARY;
// Name and index
setName( "Unnamed Weapon" );
m_fZoomSpreadMult = 1.0f;
m_bCanPartialReload = qtrue;
m_bShareClip = qfalse;
// do better lighting on all weapons
edict->s.renderfx |= RF_EXTRALIGHT;
// Weapons do not auto aim automatically
autoaim = false;
// No crosshair visible
crosshair = false;
m_bAutoZoom = false;
m_iZoom = 0;
m_bSemiAuto = false;
m_bShouldReload = false;
// Set the stats
m_iNumHits = 0;
m_iNumGroinShots = 0;
m_iNumHeadShots = 0;
m_iNumLeftArmShots = 0;
m_iNumRightArmShots = 0;
m_iNumLeftLegShots = 0;
m_iNumRightLegShots = 0;
m_iNumTorsoShots = 0;
m_fMovementSpeed = 1.0f;
m_sAmmoPickupSound = "snd_pickup_";
m_NoAmmoSound = "snd_noammo";
// Set the default weapon group
m_csWeaponGroup = STRING_EMPTY;
// Weapons default to making noise
next_noise_time = 0;
next_noammo_time = 0;
// Used to keep track of last angles and scale before holstering
lastValid = qfalse;
lastScale = 1.0f;
holsterScale = 1.0f;
// Weapon will not be putaway by default when out of ammo
auto_putaway = qfalse;
// Weapon will be able to be used when it has no ammo
use_no_ammo = qtrue;
INITIALIZE_WEAPONMODE_VAR( meansofdeath, MOD_NONE );
PostEvent( EV_Weapon_IdleInit, 0 );
}
//======================
//Weapon::Weapon
//======================
Weapon::Weapon
(
const char *file
)
{
// The tik file holds all the information available for a weapon
Weapon();
}
//======================
//Weapon::~Weapon
//======================
Weapon::~Weapon()
{
DetachGun();
entflags &= ~EF_WEAPON;
}
//======================
//Weapon::Delete
//======================
void Weapon::Delete
(
void
)
{
if( g_iInThinks )
{
DetachGun();
if( owner )
RemoveFromOwner();
PostEvent( EV_Remove, 0 );
}
else
{
delete this;
}
}
//======================
//Weapon::GetScriptOwner
//======================
Listener *Weapon::GetScriptOwner
(
void
)
{
return owner;
}
//======================
//Weapon::GetRank
//======================
int Weapon::GetRank
(
void
)
{
return rank;
}
//======================
//Weapon::GetOrder
//======================
int Weapon::GetOrder
(
void
)
{
return order;
}
//======================
//Weapon::SetRank
//======================
void Weapon::SetRank
(
int order,
int rank
)
{
this->order = order;
this->rank = rank;
}
//======================
//Weapon::SetRankEvent
//======================
void Weapon::SetRankEvent
(
Event *ev
)
{
SetRank( ev->GetInteger( 1 ), ev->GetInteger( 2 ) );
}
//======================
//Weapon::SetAutoPutaway
//======================
void Weapon::SetAutoPutaway
(
Event *ev
)
{
auto_putaway = ev->GetBoolean( 1 );
}
//======================
//Weapon::SetUseNoAmmo
//======================
void Weapon::SetUseNoAmmo
(
Event *ev
)
{
use_no_ammo = ev->GetBoolean( 1 );
}
//======================
//Weapon::SetStartAmmo
//======================
void Weapon::SetStartAmmo
(
Event *ev
)
{
if( g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
startammo[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::SetDMStartAmmo
//======================
void Weapon::SetDMStartAmmo
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
startammo[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::SetMaxChargeTime
//======================
void Weapon::SetMaxChargeTime
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
max_charge_time[firemodeindex] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetMaxChargeTime
//======================
void Weapon::SetMinChargeTime
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
min_charge_time[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::GetMinChargeTime
//======================
float Weapon::GetMinChargeTime
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
return min_charge_time[ mode ];
}
//======================
//Weapon::GetMinChargeTime
//======================
float Weapon::GetMaxChargeTime
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
return max_charge_time[ mode ];
}
//======================
//Weapon::SetAmmoRequired
//======================
void Weapon::SetAmmoRequired
(
Event *ev
)
{
if( g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
ammorequired[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::SetDMAmmoRequired
//======================
void Weapon::SetDMAmmoRequired
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
ammorequired[ firemodeindex ] = ev->GetInteger( 1 );
}
//======================
//Weapon::GetStartAmmo
//======================
int Weapon::GetStartAmmo
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
return startammo[ mode ];
}
//======================
//Weapon::GetAmmoType
//======================
str Weapon::GetAmmoType
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
return ammo_type[ mode ];
}
//======================
//Weapon::SetAmmoType
//======================
void Weapon::SetAmmoType
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
if ( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) )
ammo_type[firemodeindex] = ev->GetString( 1 );
else
{
warning( "Weapon::SetAmmoType", "Invalid mode %d\n", firemodeindex );
return;
}
}
//======================
//Weapon::SetAmmoAmount
//======================
void Weapon::SetAmmoAmount
(
int amount,
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
// If the clip can hold ammo, then set the amount in the clip to the specified amount
if( ammo_clip_size[ mode ] )
ammo_in_clip[ mode ] = amount;
}
//======================
//Weapon::GetClipSize
//======================
int Weapon::GetClipSize
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
return ammo_clip_size[ mode ];
else
{
warning( "Weapon::GetClipSize", "Invalid mode %d\n", mode );
return 0;
}
}
//======================
//Weapon::UseAmmo
//======================
void Weapon::UseAmmo
(
int amount,
firemode_t mode
)
{
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if( UnlimitedAmmo( mode ) )
return;
// Remove ammo from the clip if it's available
if( ammo_clip_size[ mode ] )
{
ammo_in_clip[ mode ] -= amount;
if( ammo_in_clip[ mode ] < 0 )
{
warning( "UseAmmo", "Used more ammo than in clip.\n" );
ammo_in_clip[ mode ] = 0;
}
if( !ammo_in_clip[ mode ] )
SetShouldReload( qtrue );
owner->AmmoAmountInClipChanged( ammo_type[ mode ], ammo_in_clip[ mode ] );
}
else
{
assert( owner );
if( owner && owner->isClient() && !UnlimitedAmmo( mode ) )
{
// Remove ammo from the player's inventory
owner->UseAmmo( ammo_type[ mode ], ammorequired[ mode ] );
}
}
}
/*
//======================
//Weapon::GetMuzzlePosition
//======================
void Weapon::GetMuzzlePosition
(
Vector *position,
Vector *forward,
Vector *right,
Vector *up
)
{
orientation_t weap_or, barrel_or, orient;
Vector pos;
vec3_t mat[3]={0,0,0,0,0,0,0,0,0};
vec3_t orient[3];
int i, mi, tagnum;
assert( owner );
// We should always have an owner
if ( !owner )
{
return;
}
// Get the owner's weapon orientation ( this is custom code and doesn't use the GetTag function
// because we need to use the saved off fire_frame and fire_animation indexes from the owner
mi = owner->edict->s.modelindex;
tagnum = gi.Tag_NumForName( mi, current_attachToTag.c_str() );
if ( tagnum < 0 )
{
warning( "Weapon::GetMuzzlePosition", "Could not find tag \"%s\"", current_attachToTag.c_str() );
pos = owner->centroid;
AnglesToAxis( owner->angles, mat );
goto out;
}
// Get the orientation based on the frame and anim stored off in the owner.
// This is to prevent weird timing with getting orientations on different frames of firing
// animations and the orientations will not be consistent.
AnglesToAxis( owner->angles, owner->orientation );
orient = gi.Tag_OrientationEx( mi,
owner->CurrentAnim( legs ),
owner->CurrentFrame( legs ),
tagnum & TAG_MASK,
owner->edict->s.scale,
owner->edict->s.bone_tag,
owner->edict->s.bone_quat,
0,
0,
1.0f,
( owner->edict->s.anim & ANIM_BLEND ) != 0,
( owner->edict->s.torso_anim & ANIM_BLEND ) != 0,
owner->GetFiringAnim(),
owner->GetFiringFrame(),
0,
0,
1.0f
);
// Transform the weapon's orientation through the owner's orientation
// Player orientation is normally based on the player's view, but we need
// it to be based on the model's orientation, so we calculate it here.
AnglesToAxis( owner->angles, orient );
VectorCopy( owner->origin, weap_or.origin );
for ( i=0; i<3; i++ )
{
VectorMA( weap_or.origin, orient.origin[i], orient[i], weap_or.origin );
}
MatrixMultiply( orient.axis, orient, weap_or.axis );
// For debugging
G_DrawCoordSystem( weap_or.origin, weap_or.axis[0], weap_or.axis[1], weap_or.axis[2], 50 );
// Get the tag_barrel orientation from the weapon
if ( !this->GetRawTag( "tag_barrel", &barrel_or ) )
{
//warning( "Weapon::GetMuzzlePosition", "Could not find tag_barrel\n" );
pos = owner->centroid;
AnglesToAxis( owner->angles, mat );
goto out;
}
//gi.DPrintf( "anim:%d frame:%d\n", this->CurrentAnim(), this->CurrentFrame() );
// Translate the barrel's orientation through the weapon's orientation
VectorCopy( weap_or.origin, pos );
for ( i = 0 ; i < 3 ; i++ )
{
VectorMA( pos, barrel_or.origin[i], weap_or.axis[i], pos );
}
MatrixMultiply( barrel_or.axis, weap_or.axis, mat );
#if 0
gi.DPrintf( "own_angles: %0.2f %0.2f %0.2f\n", owner->angles[0], owner->angles[1], owner->angles[2] );
gi.DPrintf( "own_orient: %0.2f %0.2f %0.2f\n", owner->orientation[0][0], owner->orientation[0][1], owner->orientation[0][2] );
gi.DPrintf( "bone forward: %0.2f %0.2f %0.2f\n", orient.axis[0][0], orient.axis[0][1], orient.axis[0][2] );
gi.DPrintf( "barrel forward: %0.2f %0.2f %0.2f\n", barrel_or.axis[0][0], barrel_or.axis[0][1], barrel_or.axis[0][2] );
gi.DPrintf( "weapon forward: %0.2f %0.2f %0.2f\n", weap_or.axis[0][0], weap_or.axis[0][1], weap_or.axis[0][2] );
gi.DPrintf( "mat forward: %0.2f %0.2f %0.2f\n \n", mat[0][0], mat[0][1], mat[0][2] );
#endif
// For debugging
G_DrawCoordSystem( pos, mat[0], mat[1], mat[2], 30 );
// Ok - we now have a position, forward, right, and up
out:
if ( position )
{
*position = pos;
}
if ( forward )
{
*forward = mat[0];
}
if ( right )
{
*right = mat[1];
}
if ( up )
{
*up = mat[2];
}
}
*/
//======================
//Weapon::GetMuzzlePosition
//======================
void Weapon::GetMuzzlePosition
(
Vector *position,
Vector *forward,
Vector *right,
Vector *up,
Vector *vBarrelPos
)
{
orientation_t weap_or, barrel_or, orient;
//orientation_t view_or;
Vector pos, f, r, u, aim_dir;
Vector vAng;
vec3_t mat[ 3 ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int i, tagnum;
Player *owner;
//qboolean bViewShot;
owner = ( Player * )this->owner.Pointer();
assert( owner );
// We should always have an owner
if( !owner )
{
if( IsSubclassOfVehicleTurretGun() )
{
VehicleTurretGun *t = ( VehicleTurretGun * )this;
owner = ( Player * )t->GetRemoteOwner();
}
}
// If this is a crosshair or torsoaim weapon, then the dir is specified by the player's torso angles
if( crosshair )
{
Player *player;
// Use the player's torso angles to determine direction, but use the actual barrel to determine position
player = ( Player * )owner;
AngleVectors( player->GetViewAngles(), f, r, u );
if( forward )
*forward = f;
if( right )
*right = r;
if( up )
*up = u;
}
if( current_attachToTag.length() )
{
tagnum = gi.Tag_NumForName( owner->edict->tiki, current_attachToTag.c_str() );
if( tagnum < 0 )
{
warning( "Weapon::GetMuzzlePosition", "Could not find tag \"%s\"", current_attachToTag.c_str() );
pos = owner->origin;
AnglesToAxis( owner->angles, mat );
goto out;
}
// Get the orientation based on the frame and anim stored off in the owner.
// This is to prevent weird timing with getting orientations on different frames of firing
// animations and the orientations will not be consistent.
AnglesToAxis( owner->angles, owner->orientation );
orient = G_TIKI_Orientation( owner->edict, tagnum );
for( i = 0; i < 3; i++ )
{
VectorMA( weap_or.origin, orient.origin[ i ], owner->orientation[ i ], weap_or.origin );
}
MatrixMultiply( orient.axis, owner->orientation, weap_or.axis );
}
else
{
VectorCopy( origin, weap_or.origin );
AnglesToAxis( angles, weap_or.axis );
}
if( !IsSubclassOfInventoryItem() )
{
if( ( weapon_class & WEAPON_CLASS_THROWABLE ) )
{
AngleVectors( owner->GetViewAngles(), mat[ 0 ], mat[ 1 ], mat[ 2 ] );
}
pos = owner->EyePosition();
VectorCopy( pos, weap_or.origin );
}
else
{
// using the weapon's current origin
VectorCopy( origin, weap_or.origin );
AnglesToAxis( angles, weap_or.axis );
}
// For debugging
G_DrawCoordSystem( weap_or.origin, weap_or.axis[ 0 ], weap_or.axis[ 1 ], weap_or.axis[ 2 ], 50 );
pos = weap_or.origin;
// Get the tag_barrel orientation from the weapon
if( !vBarrelPos || !this->GetRawTag( "tag_barrel", &barrel_or ) )
{
//warning( "Weapon::GetMuzzlePosition", "Could not find tag_barrel\n" );
if( owner->IsSubclassOfPlayer() )
{
Player *player = ( Player * )owner;
AnglesToAxis( player->GetViewAngles(), weap_or.axis );
}
else
AnglesToAxis( owner->angles, weap_or.axis );
AxisCopy( weap_or.axis, mat );
if( vBarrelPos )
*vBarrelPos = weap_or.origin;
goto out;
}
if( owner->IsSubclassOfPlayer() && !IsSubclassOfTurretGun() )
{
if( vBarrelPos )
*vBarrelPos = origin;
}
else
{
// Translate the barrel's orientation through the weapon's orientation
VectorCopy( weap_or.origin, pos );
for( i = 0; i < 3; i++ )
{
VectorMA( pos, barrel_or.origin[ i ], weap_or.axis[ i ], pos );
}
MatrixMultiply( barrel_or.axis, weap_or.axis, mat );
if( vBarrelPos )
*vBarrelPos = pos;
}
#if 0
gi.DPrintf( "own_angles: %0.2f %0.2f %0.2f\n", owner->angles[0], owner->angles[1], owner->angles[2] );
gi.DPrintf( "own_orient: %0.2f %0.2f %0.2f\n", owner->orientation[0][0], owner->orientation[0][1], owner->orientation[0][2] );
gi.DPrintf( "bone forward: %0.2f %0.2f %0.2f\n", orient.axis[0][0], orient.axis[0][1], orient.axis[0][2] );
gi.DPrintf( "barrel forward: %0.2f %0.2f %0.2f\n", barrel_or.axis[0][0], barrel_or.axis[0][1], barrel_or.axis[0][2] );
gi.DPrintf( "weapon forward: %0.2f %0.2f %0.2f\n", weap_or.axis[0][0], weap_or.axis[0][1], weap_or.axis[0][2] );
gi.DPrintf( "mat forward: %0.2f %0.2f %0.2f\n \n", mat[0][0], mat[0][1], mat[0][2] );
#endif
// Ok - we now have a position, forward, right, and up
out:
// For debugging
G_DrawCoordSystem( pos, mat[ 0 ], mat[ 1 ], mat[ 2 ], 30 );
if( position )
{
*position = pos;
}
if( !forward && !right && !up )
{
return;
}
if( !IsSubclassOfTurretGun() && owner && owner->IsSubclassOfPlayer() )
{
// ...
// fixme?
}
if( pos != vec_zero )
{
// ffs, wondering how they fucking done it in mohaaa...
//aim_dir = barrel_or.origin - pos;
//aim_dir.normalize();
AngleVectors( owner->GetViewAngles(), aim_dir, NULL, NULL );
if( !IsSubclassOfVehicleTurretGun() )
{
goto out2;
}
Vector ang;
vectoangles( mat[ 0 ], ang );
// this is a part of the most annoying function to reverse ever
// fixme
}
// If there is a target, then use the dir based on that
if( aim_target )
{
aim_dir = aim_target->centroid - pos;
aim_dir.normalize();
if( !IsSubclassOfTurretGun() )
{
out2:
AngleVectors( aim_dir.toAngles(), mat[ 0 ], mat[ 1 ], mat[ 2 ] );
if( forward )
*forward = mat[ 0 ];
if( right )
*right = mat[ 1 ];
if( up )
*up = mat[ 2 ];
return;
}
Vector ang;
vectoangles( mat[ 0 ], ang );
// again a very annoying part to reverse
// fixme
}
// If this weapon doesn't have a crosshair specified, then use the mat from the barrel for the directions
if( !crosshair )
{
if( forward )
*forward = mat[ 0 ];
if( right )
*right = mat[ 1 ];
if( up )
*up = mat[ 2 ];
}
}
//======================
//Weapon::SetAmmoClipSize
//======================
void Weapon::SetAmmoClipSize
(
Event * ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
ammo_clip_size[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::SetAmmoInClip
//======================
void Weapon::SetAmmoInClip
(
Event * ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
ammo_in_clip[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::SetShareClip
//======================
void Weapon::SetShareClip
(
Event * ev
)
{
m_bShareClip = qtrue;
}
//======================
//Weapon::Shoot
//======================
void Weapon::Shoot
(
Event *ev
)
{
Vector pos, forward, right, up, vBarrel, delta;
firemode_t mode = FIRE_PRIMARY;
qboolean mc;
if( ev->NumArgs() > 0 )
{
mode = WeaponModeNameToNum( ev->GetString( 1 ) );
if( mode == FIRE_ERROR )
return;
}
mc = MuzzleClear();
// If we are in loopfire, we need to keep checking ammo and using it up
if( loopfire[ mode ] )
{
if( HasAmmo( mode ) && mc )
{
// Use up the appropriate amount of ammo, it's already been checked that we have enough
UseAmmo( ammorequired[ mode ], mode );
}
else
{
ForceIdle();
}
}
// If the muzzle is not clear, then change to a clear animation, otherwise punt out.
if( !mc )
{
ForceIdle();
return;
}
GetMuzzlePosition( &pos, &forward, &right, &up, &vBarrel );
if( m_fFireSpreadMultAmount[ mode ] != 0.0f )
{
float fTime = level.time - m_fFireSpreadMultTime[ mode ];
if( fTime <= m_fFireSpreadMultTimeCap[ mode ] )
{
float fDecay = fTime * m_fFireSpreadMultFalloff[ mode ];
if( m_fFireSpreadMult[ mode ] <= 0.0f )
{
m_fFireSpreadMult[ mode ] -= fDecay;
if( m_fFireSpreadMult[ mode ] > 0.0f )
{
m_fFireSpreadMult[ mode ] = 0.0f;
}
}
else
{
m_fFireSpreadMult[ mode ] -= fDecay;
if( m_fFireSpreadMult[ mode ] < 0.0f )
{
m_fFireSpreadMult[ mode ] = 0.0f;
}
}
}
else
{
m_fFireSpreadMult[ mode ] = 0.0f;
}
m_fFireSpreadMultTime[ mode ] = level.time;
}
if( firetype[ mode ] == FT_PROJECTILE )
{
ProjectileAttack( pos,
forward,
owner,
projectileModel[ mode ],
charge_fraction
);
}
else if( firetype[ mode ] == FT_BULLET || firetype[ mode ] == FT_FAKEBULLET )
{
Vector vSpread;
float fSpreadFactor;
int tracerFrequency;
float bulletDamage;
if( owner )
{
if( owner->client )
{
Player *player = ( Player * )owner.Pointer();
fSpreadFactor = player->velocity.length() / sv_runspeed->integer;
if( fSpreadFactor > 1.0f )
{
fSpreadFactor = 1.0f;
}
vSpread = bulletspreadmax[ mode ] * fSpreadFactor;
fSpreadFactor = 1.0f - fSpreadFactor;
vSpread += bulletspread[ mode ] * fSpreadFactor;
vSpread *= m_fFireSpreadMult[ mode ] + 1.0f;
if( m_iZoom )
{
if( player->IsSubclassOfPlayer() && player->IsZoomed() )
{
vSpread *= 1.0f + fSpreadFactor * ( m_fZoomSpreadMult - 1.0f );
}
}
}
}
else
{
vSpread = ( bulletspreadmax[ mode ] + bulletspread[ mode ] ) * 0.5f;
}
if( owner && owner->IsSubclassOfPlayer() )
{
if( IsSubclassOfTurretGun() )
{
tracerFrequency = 3;
bulletDamage = 45.0f;
}
else
{
tracerFrequency = 0;
bulletDamage = bulletdamage[ mode ];
}
}
else
{
tracerFrequency = tracerfrequency[ mode ];
bulletDamage = bulletdamage[ mode ];
}
if( firetype[ mode ] == FT_BULLET )
{
BulletAttack( pos,
vBarrel,
forward,
right,
up,
bulletrange[ mode ],
bulletDamage,
bulletknockback[ mode ],
0,
GetMeansOfDeath( mode ),
vSpread,
bulletcount[ mode ],
owner,
tracerFrequency,
&tracercount[ mode ],
this
);
}
else
{
FakeBulletAttack( pos,
vBarrel,
forward,
right,
up,
bulletrange[ mode ],
bulletDamage,
vSpread,
bulletcount[ mode ],
owner,
tracerFrequency,
&tracercount[ mode ] );
}
}
else if( firetype[ mode ] == FT_SPECIAL_PROJECTILE )
{
this->SpecialFireProjectile( pos,
forward,
right,
up,
owner,
projectileModel[ mode ],
charge_fraction
);
}
else if( firetype[ mode ] == FT_CLICKITEM )
{
ClickItemAttack( pos,
forward,
bulletrange[ mode ],
owner );
}
else if( firetype[ mode ] == FT_HEAVY )
{
if( owner || !IsSubclassOfVehicleTurretGun() )
{
HeavyAttack(
pos,
forward,
projectileModel[ mode ],
0,
owner,
this );
}
else
{
VehicleTurretGun *turret = ( VehicleTurretGun * )this;
if( turret->UseRemoteControl() && turret->GetRemoteOwner() )
{
HeavyAttack(
pos,
forward,
projectileModel[ mode ],
0,
turret->GetRemoteOwner(),
this );
}
else
{
HeavyAttack(
pos,
forward,
projectileModel[ mode ],
0,
this,
this );
}
}
}
else if( firetype[ mode ] == FT_MELEE ) // this is a weapon that fires like a sword
{
Vector melee_pos, melee_end;
Vector dir;
float damage;
meansOfDeath_t meansofdeath;
float knockback;
if( owner )
{
dir = owner->centroid - pos;
}
else
{
dir = centroid - pos;
}
dir.z = 0;
melee_pos = pos - forward * dir.length();
melee_end = melee_pos + forward * bulletrange[ mode ];
damage = bulletdamage[ mode ];
knockback = 0;
meansofdeath = GetMeansOfDeath( mode );
Container<Entity *>victimlist;
m_iNumShotsFired++;
if( MeleeAttack( melee_pos, melee_end, damage, owner, meansofdeath, 8, -8, 8, knockback, true, &victimlist ) )
{
m_iNumHits++;
m_iNumTorsoShots++;
}
}
if( !quiet[ firemodeindex ] )
{
if( next_noise_time <= level.time )
{
BroadcastAIEvent( AI_EVENT_WEAPON_FIRE );
next_noise_time = level.time + 1;
}
}
if( owner && owner->client )
{
Vector vAngles = owner->GetViewAngles();
if( viewkickmin[ mode ][ 0 ] != 0.0f || viewkickmax[ mode ][ 0 ] != 0.0f )
{
vAngles[ 0 ] += random() * ( viewkickmax[ mode ][ 0 ] - viewkickmin[ mode ][ 0 ] ) + viewkickmin[ mode ][ 0 ];
}
if( viewkickmin[ 1 ][ 0 ] != 0.0f || viewkickmax[ 1 ][ 0 ] != 0.0f )
{
vAngles[ 1 ] += random() * ( viewkickmax[ mode ][ 1 ] - viewkickmin[ mode ][ 1 ] ) + viewkickmin[ mode ][ 1 ];
}
owner->SetViewAngles( vAngles );
}
if( m_fFireSpreadMultAmount[ mode ] )
{
m_fFireSpreadMult[ mode ] += m_fFireSpreadMultAmount[ mode ];
if( m_fFireSpreadMultCap[ mode ] < 0.0f )
{
if( m_fFireSpreadMultCap[ mode ] > m_fFireSpreadMult[ mode ] )
{
m_fFireSpreadMult[ mode ] = m_fFireSpreadMultCap[ mode ];
}
else if( m_fFireSpreadMult[ mode ] > 0.0f )
{
m_fFireSpreadMult[ mode ] = 0.0f;
}
}
else if( m_fFireSpreadMult[ mode ] <= m_fFireSpreadMultCap[ mode ] &&
m_fFireSpreadMult[ mode ] < 0.0f )
{
m_fFireSpreadMult[ mode ] = 0.0f;
}
}
m_fLastFireTime = level.time;
}
//======================
//Weapon::SetAimAnim
//======================
void Weapon::SetAimAnim
(
Event *ev
)
{
str anim;
anim = ev->GetString( 1 );
aimanim = gi.Anim_NumForName( edict->tiki, anim.c_str() );
aimframe = ev->GetInteger( 2 );
}
//======================
//Weapon::SetOwner
//======================
void Weapon::SetOwner
(
Sentient *ent
)
{
assert( ent );
if ( !ent )
{
// return to avoid any buggy behaviour
return;
}
Item::SetOwner( ent );
setOrigin( vec_zero );
setAngles( vec_zero );
}
//======================
//Weapon::AttachToHand
//======================
void Weapon::AttachToHand
(
Event *ev
)
{
str tag;
if( !owner || !attached ) {
return;
}
weaponhand_t hand = WeaponHandNameToNum( ev->GetString( 1 ) );
if( hand == WEAPON_ERROR ) {
return;
}
if( hand == WEAPON_OFFHAND )
{
tag = attachToTag_offhand;
}
else
{
tag = attachToTag_main;
}
current_attachToTag = tag;
int tagnum = gi.Tag_NumForName( owner->edict->tiki, tag );
if( tagnum < 0 )
{
warning( "Weapon::AttachToHand", "Attachment of weapon '%s' to tag \"%s\": Tag Not Found\n", getName().c_str(), tag.c_str() );
}
else
{
attach( owner->entnum, tagnum );
setOrigin();
}
}
//======================
//Weapon::SetCantPartialReload
//======================
void Weapon::SetCantPartialReload
(
Event *ev
)
{
if( g_gametype->integer )
return;
m_bCanPartialReload = qfalse;
}
//======================
//Weapon::SetDMCantPartialReload
//======================
void Weapon::SetDMCantPartialReload
(
Event *ev
)
{
if( !g_gametype->integer )
return;
m_bCanPartialReload = qfalse;
}
//======================
//Weapon::AmmoAvailable
//======================
int Weapon::AmmoAvailable
(
firemode_t mode
)
{
// Returns the amount of ammo the owner has that is available for use
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
// Make sure there is an owner before querying the amount of ammo
if( owner )
{
return owner->AmmoCount( ammo_type[ mode ] );
}
else
{
if( ammo_clip_size[ mode ] )
{
return ammo_clip_size[ mode ];
}
else
{
return ammorequired[ mode ];
}
}
}
//======================
//Weapon::UnlimitedAmmo
//======================
qboolean Weapon::UnlimitedAmmo
(
firemode_t mode
)
{
if( !owner )
{
return false;
}
if( !owner->isClient() || DM_FLAG( DF_INFINITE_AMMO ) )
{
return true;
}
else if( !Q_stricmp( ammo_type[ mode ], "None" ) )
{
return true;
}
return false;
}
//======================
//Weapon::HasAmmo
//======================
qboolean Weapon::HasAmmo
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
if( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
{
warning( "Weapon::HasAmmo", "Invalid mode %d\n", mode );
return false;
}
if( UnlimitedAmmo( mode ) )
{
return true;
}
// If the weapon uses a clip, check for ammo in the right clip
if( ammo_clip_size[ mode ] && HasAmmoInClip( mode ) )
{
return true;
}
else // Otherwise check if ammo is available in general
{
if( !ammorequired[ mode ] )
return true;
return ( AmmoAvailable( mode ) >= ammorequired[ mode ] );
}
}
//======================
//Weapon::HasAmmoInClip
//======================
qboolean Weapon::HasAmmoInClip
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
if( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
warning( "Weapon::HasAmmoInClip", "Invalid mode %d\n", mode );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if( ammo_clip_size[ mode ] )
{
if( ammo_in_clip[ mode ] >= ammorequired[ mode ] )
return true;
}
else
{
if( !ammorequired[ mode ] )
return true;
return ( AmmoAvailable( mode ) >= ammorequired[ mode ] );
}
return false;
}
//======================
//Weapon::IsSemiAuto
//======================
qboolean Weapon::IsSemiAuto
(
void
)
{
return m_bSemiAuto;
}
//======================
//Weapon::GetState
//======================
weaponstate_t Weapon::GetState
(
void
)
{
return weaponstate;
}
//======================
//Weapon::ForceState
//======================
void Weapon::ForceState
(
weaponstate_t state
)
{
weaponstate = state;
}
//======================
//Weapon::MuzzleClear
//======================
qboolean Weapon::MuzzleClear
(
void
)
{
return qtrue;
}
//======================
//Weapon::ReadyToFire
//======================
qboolean Weapon::ReadyToFire
(
firemode_t mode,
qboolean playsound
)
{
if( m_iZoom && mode == FIRE_SECONDARY ) {
return qtrue;
}
// Make sure the weapon is in the ready state and the weapon has ammo
if( level.time > ( m_fLastFireTime + FireDelay( mode ) ) )
{
if( HasAmmoInClip( mode ) )
{
return qtrue;
}
if( playsound && ( level.time > next_noammo_time ) )
{
Sound( "snd_noammo" );
next_noammo_time = level.time + level.frametime + G_Random( 0.1f ) + 0.95f;
}
}
return qfalse;
}
//======================
//Weapon::PutAway
//======================
void Weapon::PutAway
(
void
)
{
// set the putaway flag to true, so the state machine know to put this weapon away
putaway = true;
}
//======================
//Weapon::DetachFromOwner
//======================
void Weapon::DetachFromOwner
(
void
)
{
DetachGun();
weaponstate = WEAPON_HOLSTERED;
}
//======================
//Weapon::AttachToOwner
//======================
void Weapon::AttachToOwner
(
weaponhand_t hand
)
{
AttachGun( hand );
ForceIdle();
}
//======================
//Weapon::AttachToHolster
//======================
void Weapon::AttachToHolster
(
weaponhand_t hand
)
{
AttachGun( hand, qtrue );
SetWeaponAnim( "holster", EV_Weapon_Idle );
}
//======================
//Weapon::Drop
//======================
qboolean Weapon::Drop
(
void
)
{
Vector temp;
if( !owner )
{
return false;
}
if( !IsDroppable() )
{
return false;
}
if( attached )
{
Vector vAng;
orientation_t oTag;
Entity *pParent;
if( edict->s.parent == ENTITYNUM_NONE || edict->s.tag_num < 0 )
{
pParent = owner;
vAng = pParent->angles;
}
else
{
vAng = vec_zero;
pParent = G_GetEntity( edict->s.parent );
AnglesToAxis( pParent->angles, pParent->orientation );
pParent->GetTag( edict->s.tag_num, &oTag );
MatrixToEulerAngles( oTag.axis, vAng );
}
setAngles( vAng );
DetachGun();
}
else
{
temp[ 2 ] = 40;
setOrigin( owner->origin + temp );
setAngles( owner->angles );
}
setSize( Vector( -12, -12, -2 ), Vector( 12, 12, 12 ) );
// stop animating
StopWeaponAnim();
// drop the weapon
PlaceItem();
temp = centroid - owner->centroid;
temp[ 2 ] = 0;
VectorNormalize( temp );
temp *= 75.0f;
temp[ 0 ] += G_CRandom( 25 );
temp[ 1 ] += G_CRandom( 25 );
temp[ 2 ] = G_CRandom( 50 ) + 150;
velocity = owner->velocity * 0.5 + temp;
avelocity = Vector( 0, G_CRandom( 120 ), 0 );
spawnflags |= DROPPED_PLAYER_ITEM;
if( owner && owner->isClient() )
{
if( owner->deadflag && g_dropclips->integer > 0 )
{
int ammo;
if( ammo_clip_size[ FIRE_PRIMARY ] )
startammo[ FIRE_PRIMARY ] = g_dropclips->integer * ammo_in_clip[ FIRE_PRIMARY ];
else
startammo[ FIRE_PRIMARY ] = g_dropclips->integer;
ammo = AmmoAvailable( FIRE_PRIMARY );
if( startammo[ FIRE_PRIMARY ] > ammo )
{
startammo[ FIRE_PRIMARY ] = ammo;
}
if( ammo_clip_size[ FIRE_SECONDARY ] )
startammo[ FIRE_SECONDARY ] = g_dropclips->integer * ammo_in_clip[ FIRE_SECONDARY ];
else
startammo[ FIRE_SECONDARY ] = g_dropclips->integer;
ammo = AmmoAvailable( FIRE_SECONDARY );
if( startammo[ FIRE_SECONDARY ] > ammo )
{
startammo[ FIRE_SECONDARY ] = ammo;
}
ammo_in_clip[ FIRE_PRIMARY ] = 0;
ammo_in_clip[ FIRE_SECONDARY ] = 0;
}
else
{
startammo[ FIRE_PRIMARY ] = AmmoAvailable( FIRE_PRIMARY );
owner->takeAmmoType( ammo_type[ FIRE_PRIMARY ] );
}
}
else
{
if( ammo_clip_size[ FIRE_PRIMARY ] && ammo_in_clip[ FIRE_PRIMARY ] )
startammo[ FIRE_PRIMARY ] = ammo_in_clip[ FIRE_PRIMARY ];
else
startammo[ FIRE_PRIMARY ] >>= 2;
if( ammo_clip_size[ FIRE_SECONDARY ] && ammo_in_clip[ FIRE_SECONDARY ] )
startammo[ FIRE_SECONDARY ] = ammo_in_clip[ FIRE_SECONDARY ];
else
startammo[ FIRE_SECONDARY ] >>= 2;
if( startammo[ FIRE_PRIMARY ] == 0 )
{
startammo[ FIRE_PRIMARY ] = 1;
}
if( startammo[ FIRE_SECONDARY ] == 0 )
{
startammo[ FIRE_SECONDARY ] = 1;
}
}
// Wait some time before the last owner can pickup this weapon
last_owner = owner;
last_owner_trigger_time = level.time + 2.0f;
// Cancel reloading events
CancelEventsOfType( EV_Weapon_DoneReloading );
// Remove this from the owner's item list
RemoveFromOwner();
PostEvent( EV_Remove, g_droppeditemlife->value );
PostEvent( EV_Weapon_FallingAngleAdjust, level.frametime );
return true;
}
//======================
//Weapon::Charge
//======================
void Weapon::Charge
(
firemode_t mode
)
{
}
//======================
//Weapon::ReleaseFire
//======================
void Weapon::ReleaseFire
(
firemode_t mode,
float charge_time
)
{
// Calculate and store off the charge fraction to use when the weapon actually shoots
// Clamp to max_charge_time
if( charge_time - min_charge_time[ mode ] >= 0.0f )
{
if( charge_time <= max_charge_time[ mode ] )
charge_fraction = charge_time / max_charge_time[ mode ];
else
charge_fraction = 1.0f;
}
else
{
charge_fraction = 0.0f;
}
// Call regular fire function
Fire( mode );
}
//======================
//Weapon::Fire
//======================
void Weapon::Fire
(
firemode_t mode
)
{
Event *done_event = NULL;
Vector pos;
// Sanity check the mode
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
// If we are in loopfire mode, then we don't pass a DoneFiring event
if( !loopfire[ mode ] )
{
// The DoneFiring event requires to know the firing mode so save that off in the event
done_event = new Event( EV_Weapon_DoneFiring );
done_event->AddInteger( mode );
}
if( !MuzzleClear() )
{
SetWeaponAnim( "clear", done_event );
weaponstate = WEAPON_READY;
return;
}
// Use up the appropriate amount of ammo, it's already been checked that we have enough
UseAmmo( ammorequired[ mode ], mode );
// Set the state of the weapon to FIRING
weaponstate = WEAPON_FIRING;
// Cancel any old done firing events
CancelEventsOfType( EV_Weapon_DoneFiring );
// Play the correct animation
if( mode == FIRE_PRIMARY )
{
if( ammo_clip_size[ FIRE_PRIMARY ] && !ammo_in_clip[ FIRE_PRIMARY ] && HasAnim( "fire_empty" ) )
{
SetWeaponAnim( "fire_empty", done_event );
}
else
{
SetWeaponAnim( "fire", done_event );
}
}
else if( mode == FIRE_SECONDARY )
{
if( ( ( !m_bShareClip && ammo_clip_size[ FIRE_SECONDARY ] && !ammo_in_clip[ FIRE_SECONDARY ] ) ||
( m_bShareClip && ammo_clip_size[ FIRE_PRIMARY ] && !ammo_in_clip[ FIRE_PRIMARY ] ) ) && HasAnim( "fire_empty" ) )
{
SetWeaponAnim( "secondaryfire_empty", done_event );
}
else
{
SetWeaponAnim( "secondaryfire", done_event );
}
}
}
//======================
//Weapon::DetachGun
//======================
void Weapon::DetachGun
(
void
)
{
if( attached )
{
if( m_iZoom && owner && owner->IsSubclassOfPlayer() )
{
Player *p = ( Player * )owner.Pointer();
p->ZoomOff();
}
StopSound( CHAN_WEAPONIDLE );
attached = false;
detach();
hideModel();
}
}
//======================
//Weapon::AttachGun
//======================
void Weapon::AttachGun
(
weaponhand_t hand,
qboolean holstering
)
{
int tag_num;
if( !owner )
{
current_attachToTag = "";
return;
}
if( attached )
{
DetachGun();
}
if( holstering )
{
// Save off these values if we are holstering the weapon. We will restore them when
// the users raises the weapons again.
lastAngles = this->angles;
lastScale = this->edict->s.scale;
lastValid = qtrue;
}
else if( lastValid )
{
// Restore the last
setScale( lastScale );
setAngles( lastAngles );
lastValid = qfalse;
}
switch( hand )
{
case WEAPON_MAIN:
if( holstering )
{
current_attachToTag = holster_attachToTag;
setAngles( holsterAngles );
setScale( holsterScale );
}
else
{
current_attachToTag = attachToTag_main;
}
break;
case WEAPON_OFFHAND:
if( holstering )
{
current_attachToTag = holster_attachToTag;
setAngles( holsterAngles );
setScale( holsterScale );
}
else
{
current_attachToTag = attachToTag_offhand;
}
break;
default:
warning( "Weapon::AttachGun", "Invalid hand for attachment of weapon specified" );
break;
}
if( !current_attachToTag.length() )
return;
if( owner->edict->tiki )
{
tag_num = gi.Tag_NumForName( owner->edict->tiki, this->current_attachToTag.c_str() );
NoLerpThisFrame();
if( tag_num >= 0 )
{
attached = true;
attach( owner->entnum, tag_num );
showModel();
setOrigin();
}
else
{
warning( "Weapon::AttachGun", "Attachment of weapon to tag \"%s\": Tag Not Found\n", this->current_attachToTag.c_str() );
}
}
if( m_bAutoZoom && owner->IsSubclassOfPlayer() )
{
Player *p = ( Player * )owner.Pointer();
p->ToggleZoom( m_iZoom );
}
}
//======================
//Weapon::GiveStartingAmmoToOwner
//======================
void Weapon::GiveStartingAmmoToOwner
(
Event *ev
)
{
str ammotype;
int mode;
assert( owner );
if( !owner )
{
warning( "Weapon::GiveStartingAmmoToOwner", "Owner not found\n" );
return;
}
// Give the player the starting ammo
for( mode = FIRE_PRIMARY; mode<MAX_FIREMODES; mode++ )
{
ammotype = GetAmmoType( ( firemode_t )( mode ) );
if( ammotype.length() )
{
int start_ammo = this->GetStartAmmo( ( firemode_t )mode );
if( ammo_clip_size[ mode ] )
{
int ammo = ammo_clip_size[ mode ] - ammo_in_clip[ mode ];
if( ammo > 0 )
{
if( ammo < start_ammo )
{
start_ammo -= ammo;
ammo_in_clip[ mode ] = ammo + ammo_in_clip[ mode ];
}
else
{
ammo_in_clip[ mode ] = start_ammo + ammo_in_clip[ mode ];
start_ammo = 0;
}
}
}
if( start_ammo )
{
owner->GiveAmmo( ammotype, start_ammo );
}
}
}
}
//======================
//Weapon::PickupWeapon
//======================
void Weapon::PickupWeapon
(
Event *ev
)
{
Sentient *sen;
Entity *other;
qboolean hasweapon;
other = ev->GetEntity( 1 );
assert( other );
if( !other->IsSubclassOfSentient() )
{
return;
}
sen = ( Sentient * )other;
// If this is the last owner, check to see if he can pick it up
if( ( sen == last_owner ) && ( level.time < last_owner_trigger_time ) )
{
return;
}
hasweapon = sen->HasItem( item_name ) || sen->HasWeaponClass( weapon_class );
if( g_gametype->integer && !hasweapon && !IsSecondaryWeapon() )
{
// Make sure the sentient doesn't have a primary weapon on DM modes
hasweapon = sen->HasPrimaryWeapon();
}
if( !hasweapon )
{
if( other->IsSubclassOfPlayer() )
{
gi.SendServerCommand( other->edict - g_entities, "print \"" HUD_MESSAGE_YELLOW "%s\n\"", gi.LV_ConvertString( va( "Picked Up %s", item_name.c_str() ) ) );
if( !( spawnflags & DROPPED_PLAYER_ITEM ) && !( spawnflags & DROPPED_ITEM ) )
{
ItemPickup( other );
return;
}
if( Pickupable( other ) )
{
setMoveType( MOVETYPE_NONE );
setSolidType( SOLID_NOT );
hideModel();
velocity = vec_zero;
avelocity = vec_zero;
CancelEventsOfType( EV_Remove );
CancelEventsOfType( EV_Weapon_FallingAngleAdjust );
StopWeaponAnim();
DetachGun();
ForceState( WEAPON_CHANGING );
current_attachToTag = "";
lastValid = qfalse;
edict->s.tag_num = -1;
edict->s.attach_use_angles = qfalse;
VectorClear( edict->s.attach_offset );
setOrigin( vec_zero );
setAngles( vec_zero );
SetOwner( sen );
sen->AddItem( this );
sen->ReceivedItem( this );
Sound( sPickupSound );
}
}
}
else
{
str sAmmoName = ammo_type[ FIRE_PRIMARY ];
if( sen->AmmoCount( sAmmoName ) != sen->MaxAmmoCount( sAmmoName ) )
{
setSolidType( SOLID_NOT );
hideModel();
CancelEventsOfType( EV_Item_DropToFloor );
CancelEventsOfType( EV_Item_Respawn );
CancelEventsOfType( EV_FadeOut );
CancelEventsOfType( EV_Remove );
CancelEventsOfType( EV_Weapon_FallingAngleAdjust );
if( Respawnable() )
{
PostEvent( EV_Item_Respawn, 0 );
}
else
{
PostEvent( EV_Remove, 0 );
}
Sound( m_sAmmoPickupSound );
}
}
if( sen->isClient() && startammo[ FIRE_PRIMARY ] && ammo_type[ FIRE_PRIMARY ].length() )
{
str sMessage;
str sAmmoType = ammo_type[ FIRE_PRIMARY ];
sen->GiveAmmo( sAmmoType, startammo[ FIRE_PRIMARY ] );
if( !sAmmoType.icmp( "agrenade" ) )
{
if( startammo[ FIRE_PRIMARY ] == 1 )
sMessage = gi.LV_ConvertString( "Got 1 Grenade" );
else
sMessage = gi.LV_ConvertString( va( "Got %i Grenades", startammo[ FIRE_PRIMARY ] ) );
}
else
{
sMessage = gi.LV_ConvertString( va( "Got %i %s Rounds", startammo[ FIRE_PRIMARY ], sAmmoType.c_str() ) );
}
gi.SendServerCommand( other->edict - g_entities, "print \"" HUD_MESSAGE_YELLOW "%s\n\"", sMessage.c_str() );
}
Unregister( STRING_PICKUP );
}
//======================
//Weapon::IsSecondaryWeapon
//======================
qboolean Weapon::IsSecondaryWeapon
(
void
)
{
return ( weapon_class & WEAPON_CLASS_SECONDARY );
}
//======================
//Weapon::ForceIdle
//======================
void Weapon::ForceIdle
(
void
)
{
SetWeaponIdleAnim();
// Force the weapon to the idle animation
weaponstate = WEAPON_READY;
}
//======================
//Weapon::SetWeaponAnim
//======================
qboolean Weapon::SetWeaponAnim
(
const char *anim,
Event *ev
)
{
int animnum = gi.Anim_NumForName( edict->tiki, anim );
if( animnum == -1 )
{
if( ev ) {
delete ev;
}
return qfalse;
}
StopAnimating( m_iAnimSlot );
SetTime( m_iAnimSlot );
int idleanim = gi.Anim_NumForName( edict->tiki, "idle" );
edict->s.frameInfo[ m_iAnimSlot ].index = idleanim;
m_iAnimSlot = ( m_iAnimSlot + 1 ) & 3;
edict->s.frameInfo[ m_iAnimSlot ].index = idleanim;
if( ev )
{
NewAnim( animnum, ev, m_iAnimSlot );
}
else
{
NewAnim( animnum, m_iAnimSlot );
}
SetOnceType( m_iAnimSlot );
SetTime( m_iAnimSlot );
return qtrue;
}
//======================
//Weapon::SetWeaponAnim
//======================
qboolean Weapon::SetWeaponAnim
(
const char *anim,
Event& ev
)
{
Event *event = new Event( ev );
return SetWeaponAnim( anim, event );
}
//======================
//Weapon::SetWeaponAnimEvent
//======================
void Weapon::SetWeaponAnimEvent
(
Event *ev
)
{
SetWeaponAnim( ev->GetString( 1 ) );
}
//======================
//Weapon::SetWeaponIdleAnim
//======================
void Weapon::SetWeaponIdleAnim
(
void
)
{
if( ammo_clip_size[ FIRE_PRIMARY ] && !ammo_in_clip[ FIRE_PRIMARY ] )
{
if( SetWeaponAnim( "idle_empty" ) )
{
return;
}
}
SetWeaponAnim( "idle" );
}
//======================
//Weapon::SetWeaponIdleAnimEvent
//======================
void Weapon::SetWeaponIdleAnimEvent
(
Event *ev
)
{
SetWeaponIdleAnim();
}
//======================
//Weapon::StopWeaponAnim
//======================
void Weapon::StopWeaponAnim
(
void
)
{
SetTime( m_iAnimSlot );
StopAnimating( m_iAnimSlot );
int animnum = gi.Anim_NumForName( edict->tiki, "idle" );
edict->s.frameInfo[ m_iAnimSlot ].index = animnum;
edict->s.frameInfo[ m_iAnimSlot ].weight = 1.0f;
edict->s.frameInfo[ m_iAnimSlot ].time = 0;
m_iAnimSlot = ( m_iAnimSlot + 1 ) & 3;
}
//======================
//Weapon::DoneRaising
//======================
void Weapon::DoneRaising
(
Event *ev
)
{
weaponstate = WEAPON_READY;
ForceIdle();
if( !owner )
{
PostEvent( EV_Remove, 0 );
return;
}
}
//======================
//Weapon::ClientFireDone
//======================
void Weapon::ClientFireDone
(
void
)
{
// This is called when the client's firing animation is done
}
//======================
//Weapon::DoneFiring
//======================
void Weapon::DoneFiring
(
Event *ev
)
{
// This is called when the weapon's firing animation is done
ForceIdle();
// Check to see if the auto_putaway flag is set, and the weapon is out of ammo. If so, then putaway the
// weapon.
if( !HasAmmo( FIRE_PRIMARY ) && auto_putaway )
{
PutAway();
}
}
//======================
//Weapon::DoneReloading
//======================
void Weapon::DoneReloading
(
Event *ev
)
{
SetShouldReload( qfalse );
weaponstate = WEAPON_READY;
}
//======================
//Weapon::CheckReload
//======================
qboolean Weapon::CheckReload
(
firemode_t mode
)
{
// Check to see if the weapon needs to be reloaded
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if( putaway )
return false;
if( ammo_in_clip[ mode ] < ammo_clip_size[ mode ] && AmmoAvailable( mode ) && ( m_bCanPartialReload || ammo_in_clip[ mode ] <= 0 ) )
return true;
return false;
}
//======================
//Weapon::Idle
//======================
void Weapon::Idle
(
Event *ev
)
{
ForceIdle();
}
//======================
//Weapon::IdleInit
//======================
void Weapon::IdleInit
(
Event *ev
)
{
for( int i = 7; i >= 0; i-- )
{
SetWeaponAnim( "idle" );
}
weaponstate = WEAPON_READY;
}
//======================
//Weapon::GetMaxRange
//======================
float Weapon::GetMaxRange
(
void
)
{
return maxrange;
}
//======================
//Weapon::GetMinRange
//======================
float Weapon::GetMinRange
(
void
)
{
return minrange;
}
//======================
//Weapon::SetMaxRangeEvent
//======================
void Weapon::SetMaxRangeEvent
(
Event *ev
)
{
maxrange = ev->GetFloat( 1 );
}
//======================
//Weapon::SetMinRangeEvent
//======================
void Weapon::SetMinRangeEvent
(
Event *ev
)
{
minrange = ev->GetFloat( 1 );
}
//======================
//Weapon::NotDroppableEvent
//======================
void Weapon::NotDroppableEvent
(
Event *ev
)
{
notdroppable = true;
}
//======================
//Weapon::SetMaxRange
//======================
void Weapon::SetMaxRange
(
float val
)
{
maxrange = val;
}
//======================
//Weapon::SetMinRange
//======================
void Weapon::SetMinRange
(
float val
)
{
minrange = val;
}
//======================
//Weapon::WeaponSound
//======================
void Weapon::WeaponSound
(
Event *ev
)
{
Event *e;
// Broadcasting a sound can be time consuming. Only do it once in a while on really fast guns.
if( nextweaponsoundtime > level.time )
{
if( owner )
{
owner->BroadcastAIEvent( AI_EVENT_WEAPON_FIRE );
}
else
{
BroadcastAIEvent( AI_EVENT_WEAPON_FIRE );
}
return;
}
if( owner )
{
e = new Event( ev );
owner->ProcessEvent( e );
}
else
{
Item::BroadcastAIEvent( AI_EVENT_WEAPON_FIRE );
}
// give us some breathing room
nextweaponsoundtime = level.time + 0.4;
}
//======================
//Weapon::Removable
//======================
qboolean Weapon::Removable
(
void
)
{
if (
( ( int )( dmflags->integer ) & DF_WEAPONS_STAY ) &&
!( spawnflags & ( DROPPED_ITEM | DROPPED_PLAYER_ITEM ) )
)
return false;
else
return true;
}
//======================
//Weapon::Pickupable
//======================
qboolean Weapon::Pickupable
(
Entity *other
)
{
Sentient *sen;
if ( !other->IsSubclassOfSentient() )
{
return false;
}
else if ( !other->isClient() )
{
return false;
}
sen = ( Sentient * )other;
//FIXME
// This should be in player
// If we have the weapon and weapons stay, then don't pick it up
if ( ( ( int )( dmflags->integer ) & DF_WEAPONS_STAY ) && !( spawnflags & ( DROPPED_ITEM | DROPPED_PLAYER_ITEM ) ) )
{
Weapon *weapon;
weapon = ( Weapon * )sen->FindItem( getName() );
if ( weapon )
return false;
}
return true;
}
//======================
//Weapon::AutoChange
//======================
qboolean Weapon::AutoChange
(
void
)
{
return true;
}
//======================
//Weapon::ClipAmmo
//======================
int Weapon::ClipAmmo
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if( ammo_clip_size[ mode ] )
return ammo_in_clip[ mode ];
else
return -1;
}
//======================
//Weapon::ProcessWeaponCommandsEvent
//======================
void Weapon::ProcessWeaponCommandsEvent
(
Event *ev
)
{
int index;
index = ev->GetInteger( 1 );
ProcessInitCommands();
}
//======================
//Weapon::SetActionLevelIncrement
//======================
void Weapon::SetActionLevelIncrement
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
action_level_increment[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::ActionLevelIncrement
//======================
int Weapon::ActionLevelIncrement
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
if ( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
return action_level_increment[mode];
else
{
warning( "Weapon::ActionLevelIncrement", "Invalid mode %d\n", mode );
return 0;
}
}
//======================
//Weapon::IsDroppable
//======================
qboolean Weapon::IsDroppable
(
void
)
{
if( notdroppable )
{
return false;
}
else
{
return true;
}
}
//======================
//Weapon::SetFireType
//======================
void Weapon::SetFireType
(
Event *ev
)
{
str ftype;
ftype = ev->GetString( 1 );
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
if( !ftype.icmp( "projectile" ) )
firetype[ firemodeindex ] = FT_PROJECTILE;
else if( !ftype.icmp( "bullet" ) )
firetype[ firemodeindex ] = FT_BULLET;
else if( !ftype.icmp( "fakebullet" ) )
firetype[ firemodeindex ] = FT_FAKEBULLET;
else if( !ftype.icmp( "melee" ) )
firetype[ firemodeindex ] = FT_MELEE;
else if( !ftype.icmp( "special_projectile" ) )
firetype[ firemodeindex ] = FT_SPECIAL_PROJECTILE;
else if( !ftype.icmp( "clickitem" ) )
firetype[ firemodeindex ] = FT_CLICKITEM;
else if( !ftype.icmp( "heavy" ) )
firetype[ firemodeindex ] = FT_HEAVY;
else if( !ftype.icmp( "none" ) )
firetype[ firemodeindex ] = FT_NONE;
else
warning( "Weapon::SetFireType", "unknown firetype: %s\n", ftype.c_str() );
}
//======================
//Weapon::GetFireType
//======================
firetype_t Weapon::GetFireType
(
firemode_t mode
)
{
return firetype[mode];
}
firemode_t Weapon::GetFireMode()
{
return firemodeindex;
}
//======================
//Weapon::SetProjectile
//======================
void Weapon::SetProjectile
(
Event *ev
)
{
if( g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
projectileModel[ firemodeindex ] = ev->GetString( 1 );
CacheResource( projectileModel[ firemodeindex ].c_str() );
}
//======================
//Weapon::SetDMProjectile
//======================
void Weapon::SetDMProjectile
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
projectileModel[ firemodeindex ] = ev->GetString( 1 );
CacheResource( projectileModel[ firemodeindex ].c_str() );
}
//======================
//Weapon::SetBulletDamage
//======================
void Weapon::SetBulletDamage
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletdamage[firemodeindex] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetDMBulletDamage
//======================
void Weapon::SetDMBulletDamage
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletdamage[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetBulletKnockback
//======================
void Weapon::SetBulletKnockback
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletknockback[firemodeindex] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetDMBulletKnockback
//======================
void Weapon::SetDMBulletKnockback
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletknockback[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetBulletRange
//======================
void Weapon::SetBulletRange
(
Event *ev
)
{
if( g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletrange[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetDMBulletRange
//======================
void Weapon::SetDMBulletRange
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletrange[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetRange
//======================
void Weapon::SetRange
(
Event *ev
)
{
SetBulletRange( ev );
}
//======================
//Weapon::SetBulletCount
//======================
void Weapon::SetBulletCount
(
Event *ev
)
{
if( g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletcount[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetDMBulletCount
//======================
void Weapon::SetDMBulletCount
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletcount[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetBulletSpread
//======================
void Weapon::SetBulletSpread
(
Event *ev
)
{
if( g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletspread[ firemodeindex ].x = ev->GetFloat( 1 );
bulletspread[ firemodeindex ].y = ev->GetFloat( 2 );
if( ev->NumArgs() > 2 )
{
bulletspreadmax[ firemodeindex ].x = ev->GetFloat( 3 );
bulletspreadmax[ firemodeindex ].y = ev->GetFloat( 4 );
}
}
//======================
//Weapon::SetDMBulletSpread
//======================
void Weapon::SetDMBulletSpread
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletspread[ firemodeindex ].x = ev->GetFloat( 1 );
bulletspread[ firemodeindex ].y = ev->GetFloat( 2 );
if( ev->NumArgs() > 2 )
{
bulletspreadmax[ firemodeindex ].x = ev->GetFloat( 3 );
bulletspreadmax[ firemodeindex ].y = ev->GetFloat( 4 );
}
}
//======================
//Weapon::SetZoomSpreadMult
//======================
void Weapon::SetZoomSpreadMult
(
Event *ev
)
{
if( g_gametype->integer )
return;
m_fZoomSpreadMult = ev->GetFloat( 1 );
}
//======================
//Weapon::SetDMZoomSpreadMult
//======================
void Weapon::SetDMZoomSpreadMult
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
m_fZoomSpreadMult = ev->GetFloat( 1 );
}
//======================
//Weapon::SetZoomSpreadMult
//======================
void Weapon::SetFireSpreadMult
(
Event *ev
)
{
if( g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
m_fFireSpreadMultAmount[ firemodeindex ] = ev->GetFloat( 1 );
m_fFireSpreadMultFalloff[ firemodeindex ] = ev->GetFloat( 2 );
m_fFireSpreadMultCap[ firemodeindex ] = ev->GetFloat( 3 );
m_fFireSpreadMultTimeCap[ firemodeindex ] = ev->GetFloat( 4 );
}
//======================
//Weapon::SetDMZoomSpreadMult
//======================
void Weapon::SetDMFireSpreadMult
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
m_fFireSpreadMultAmount[ firemodeindex ] = ev->GetFloat( 1 );
m_fFireSpreadMultFalloff[ firemodeindex ] = ev->GetFloat( 2 );
m_fFireSpreadMultCap[ firemodeindex ] = ev->GetFloat( 3 );
m_fFireSpreadMultTimeCap[ firemodeindex ] = ev->GetFloat( 4 );
}
//======================
//Weapon::SetTracerFrequency
//======================
void Weapon::SetTracerFrequency
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
tracerfrequency[ firemodeindex ] = ev->GetInteger( 1 );
}
//======================
//Weapon::Alternate
//======================
void Weapon::Secondary
(
Event *ev
)
{
int i;
Event *altev = new Event( ev->GetToken( 1 ) );
firemodeindex = FIRE_SECONDARY;
for( i = 2; i <= ev->NumArgs(); i++ )
{
altev->AddToken( ev->GetToken( i ) );
}
ProcessEvent( altev );
firemodeindex = FIRE_PRIMARY;
}
//====================
//Weapon::AutoAim
//====================
void Weapon::AutoAim
(
Event *ev
)
{
autoaim = true;
}
//====================
//Weapon::Crosshair
//====================
void Weapon::Crosshair
(
Event *ev
)
{
crosshair = ev->GetBoolean( 1 );
}
//====================
//Weapon::DMCrosshair
//====================
void Weapon::DMCrosshair
(
Event *ev
)
{
if( !g_gametype->integer )
return;
crosshair = ev->GetBoolean( 1 );
}
//====================
//Weapon::OffHandAttachToTag
//====================
void Weapon::OffHandAttachToTag
(
Event *ev
)
{
attachToTag_offhand = ev->GetString( 1 );
}
//====================
//Weapon::MainAttachToTag
//====================
void Weapon::MainAttachToTag
(
Event *ev
)
{
attachToTag_main = ev->GetString( 1 );
}
//====================
//Weapon::HolsterAttachToTag
//====================
void Weapon::HolsterAttachToTag
(
Event *ev
)
{
holster_attachToTag = ev->GetString( 1 );
}
//====================
//Weapon::SetHolsterOffset
//====================
void Weapon::SetHolsterOffset
(
Event *ev
)
{
holsterOffset = ev->GetVector( 1 );
}
//====================
//Weapon::SetHolsterAngles
//====================
void Weapon::SetHolsterAngles
(
Event *ev
)
{
holsterAngles = ev->GetVector( 1 );
}
//====================
//Weapon::SetHolsterScale
//====================
void Weapon::SetHolsterScale
(
Event *ev
)
{
holsterScale = ev->GetFloat( 1 );
}
//====================
//Weapon::SetQuiet
//====================
void Weapon::SetQuiet
(
Event *ev
)
{
quiet[ firemodeindex ] = true;
}
//====================
//Weapon::SetLoopFire
//====================
void Weapon::SetLoopFire
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
loopfire[firemodeindex] = true;
}
//====================
//Weapon::GetZoom
//====================
int Weapon::GetZoom( void )
{
return m_iZoom;
}
//====================
//Weapon::GetAutoZoom
//====================
qboolean Weapon::GetAutoZoom( void )
{
return m_bAutoZoom;
}
//======================
//Weapon::SetMeansOfDeath
//======================
void Weapon::SetMeansOfDeath
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
meansofdeath[firemodeindex] = (meansOfDeath_t )MOD_NameToNum( ev->GetString( 1 ) );
}
//======================
//Weapon::GetMeansOfDeath
//======================
meansOfDeath_t Weapon::GetMeansOfDeath
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
if( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
return meansofdeath[ mode ];
else
{
warning( "Weapon::GetMeansOfDeath", "Invalid mode %d\n", mode );
return MOD_NONE;
}
}
//======================
//Weapon::SetAimTarget
//======================
void Weapon::SetAimTarget
(
Entity *ent
)
{
aim_target = ent;
}
qboolean Weapon::ShouldReload( void )
{
if( m_bShouldReload )
{
return qtrue;
}
else
{
if( ammo_clip_size[ FIRE_PRIMARY ] && !ammo_in_clip[ FIRE_PRIMARY ] && AmmoAvailable( FIRE_PRIMARY ) )
return qtrue;
}
return qfalse;
}
void Weapon::SetShouldReload( qboolean should_reload )
{
m_bShouldReload = should_reload;
}
void Weapon::StartReloading( void )
{
if( !ammo_clip_size[ 0 ] || !owner ) {
return;
}
Event ev( EV_Weapon_DoneReloading );
if( SetWeaponAnim( "reload", ev ) )
{
weaponstate = WEAPON_RELOADING;
}
else
{
ProcessEvent( EV_Weapon_FillClip );
ProcessEvent( EV_Weapon_DoneReloading );
}
m_fFireSpreadMult[ FIRE_PRIMARY ] = 0;
}
//======================
//Weapon::WorldHitSpawn
//======================
void Weapon::WorldHitSpawn
(
firemode_t mode,
Vector origin,
Vector angles,
float life
)
{
}
//======================
//Weapon::SetWorldHitSpawn
//======================
void Weapon::SetWorldHitSpawn
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
worldhitspawn[firemodeindex] = ev->GetString( 1 );
}
void Weapon::SetWeaponGroup( Event *ev )
{
m_csWeaponGroup = ev->GetConstString( 1 );
}
void Weapon::SetZoom( Event *ev )
{
m_iZoom = ev->GetInteger( 1 );
if( ev->NumArgs() > 1 )
{
int autozoom = ev->GetInteger( 2 );
if( autozoom ) {
m_bAutoZoom = qtrue;
}
}
}
void Weapon::SetSemiAuto( Event *ev )
{
m_bSemiAuto = qtrue;
}
void Weapon::SetWeaponType( Event *ev )
{
weapon_class = G_WeaponClassNameToNum( ev->GetString( 1 ) );
}
void Weapon::EventClipAdd( Event *ev )
{
int amount;
int amount_used;
// Calc the amount the clip should get
amount = ev->GetInteger( 1 );
assert( owner );
if( owner && owner->isClient() && !UnlimitedAmmo( FIRE_PRIMARY ) )
{
if( amount > ammo_clip_size[ FIRE_PRIMARY ] )
amount = ammo_clip_size[ FIRE_PRIMARY ] - ammo_in_clip[ FIRE_PRIMARY ];
// use up the ammo from the player
amount_used = owner->UseAmmo( ammo_type[ FIRE_PRIMARY ], amount );
// Stick it in the clip
if( ammo_clip_size[ FIRE_PRIMARY ] )
ammo_in_clip[ FIRE_PRIMARY ] = amount_used + ammo_in_clip[ FIRE_PRIMARY ];
if( ammo_in_clip[ FIRE_PRIMARY ] > ammo_clip_size[ FIRE_PRIMARY ] )
ammo_in_clip[ FIRE_PRIMARY ] = ammo_clip_size[ FIRE_PRIMARY ];
owner->AmmoAmountInClipChanged( ammo_type[ FIRE_PRIMARY ], ammo_in_clip[ FIRE_PRIMARY ] );
SetShouldReload( qfalse );
}
}
void Weapon::EventClipEmpty( Event *ev )
{
if( !ammo_clip_size[ FIRE_PRIMARY ] )
return;
if( !owner )
return;
owner->GiveAmmo( ammo_type[ FIRE_PRIMARY ], ammo_in_clip[ FIRE_PRIMARY ] );
ammo_in_clip[ FIRE_PRIMARY ] = 0;
SetShouldReload( qtrue );
owner->AmmoAmountInClipChanged( ammo_type[ FIRE_PRIMARY ], ammo_in_clip[ FIRE_PRIMARY ] );
}
void Weapon::EventClipFill( Event *ev )
{
int amount;
int amount_used;
// Calc the amount the clip should get
amount = ammo_clip_size[ FIRE_PRIMARY ] - ammo_in_clip[ FIRE_PRIMARY ];
assert( owner );
if( owner && owner->isClient() && !UnlimitedAmmo( FIRE_PRIMARY ) )
{
// use up the ammo from the player
amount_used = owner->UseAmmo( ammo_type[ FIRE_PRIMARY ], amount );
// Stick it in the clip
if( ammo_clip_size[ FIRE_PRIMARY ] )
ammo_in_clip[ FIRE_PRIMARY ] = amount_used + ammo_in_clip[ FIRE_PRIMARY ];
assert( ammo_in_clip[ FIRE_PRIMARY ] <= ammo_clip_size[ FIRE_PRIMARY ] );
if( ammo_in_clip[ FIRE_PRIMARY ] > ammo_clip_size[ FIRE_PRIMARY ] )
ammo_in_clip[ FIRE_PRIMARY ] = ammo_clip_size[ FIRE_PRIMARY ];
}
owner->AmmoAmountInClipChanged( ammo_type[ FIRE_PRIMARY ], ammo_in_clip[ FIRE_PRIMARY ] );
SetShouldReload( qfalse );
}
float Weapon::FireDelay
(
firemode_t mode
)
{
return firedelay[ mode ];
}
void Weapon::EventSetFireDelay
(
Event *ev
)
{
if( g_gametype->integer )
return;
firedelay[ firemodeindex ] = ev->GetFloat( 1 );
}
void Weapon::EventSetDMFireDelay
(
Event *ev
)
{
if( !g_gametype->integer )
return;
firedelay[ firemodeindex ] = ev->GetFloat( 1 );
}
void Weapon::MakeNoise
(
Event *ev
)
{
float radius = 500;
qboolean force = false;
if( ev->NumArgs() > 0 )
radius = ev->GetFloat( 1 );
if( ev->NumArgs() > 1 )
force = ev->GetBoolean( 2 );
if( attached && ( next_noise_time <= level.time || force ) )
{
BroadcastAIEvent(AI_EVENT_MISC, radius );
next_noise_time = level.time + 1;
}
}
void Weapon::FallingAngleAdjust
(
Event *ev
)
{
Vector vTmp;
Vector vDir;
trace_t trace;
if( owner )
return;
if( groundentity && ( groundentity->entity == world || groundentity->r.bmodel ) )
{
Vector vEnd = origin - Vector( 0, 0, 128 );
trace = G_Trace( origin,
vec_zero,
vec_zero,
vEnd,
last_owner,
edict->clipmask,
qfalse,
"Weapon::FallingAngleAdjust" );
if( trace.fraction < 1.0f )
{
Vector vAng = Vector( 0, angles[ 1 ], 0 );
Vector vCross;
/*vAng.AngleVectorsLeft( &vDir );
CrossProduct( vAng, vDir, vCross );
CrossProduct( vDir, vCross, vAng );
vAng = vAng.toAngles();*/
if( angles[ 2 ] <= 0.0f )
vAng[ 2 ] = anglemod( vAng[ 2 ] - 90.0f );
else
vAng[ 2 ] = anglemod( vAng[ 2 ] + 90.0f );
setAngles( vAng );
}
if( weapon_class >= 0 )
{
Sound( G_WeaponClassNumToName( weapon_class ) + "_drop" );
}
return;
}
angles[ 0 ] = anglemod( angles[ 0 ] );
//if( angles[ 0 ] > 180.0f )
// angles[ 0 ] -= 180.0f;
if( angles[ 0 ] >= -90.0f && angles[ 0 ] <= 0.0f )
{
angles[ 0 ] -= level.frametime * 160.0f;
if( angles[ 0 ] < -180.0f )
angles[ 0 ] = -180.0f;
}
else if( angles[ 0 ] > 0.0f )
{
angles[ 0 ] -= level.frametime * 160.0f;
}
else
{
angles[ 0 ] += level.frametime * 160.0f;
}
angles[ 2 ] = anglemod( angles[ 2 ] );
//if( angles[ 2 ] > 180.0f )
// angles[ 2 ] -= 180.0f;
if( angles[ 2 ] > -90.0f && angles[ 2 ] < 0.0f )
{
angles[ 2 ] -= level.frametime * 160.0f;
}
else if( angles[ 2 ] > 90.0f )
{
angles[ 2 ] -= level.frametime * 160.0f;
}
else
{
angles[ 2 ] += level.frametime * 160.0f;
}
angles[ 0 ] = anglemod( angles[ 0 ] );
angles[ 2 ] = anglemod( angles[ 2 ] );
setAngles( angles );
PostEvent( EV_Weapon_FallingAngleAdjust, level.frametime );
}
void Weapon::SetAIRange
(
Event *ev
)
{
str s = ev->GetString( 1 );
if( !s.icmp( "short" ) )
{
mAIRange = RANGE_SHORT;
}
else if( !s.icmp( "medium" ) )
{
mAIRange = RANGE_MEDIUM;
}
else if( !s.icmp( "long" ) )
{
mAIRange = RANGE_LONG;
}
else if( !s.icmp( "sniper" ) )
{
mAIRange = RANGE_SNIPER;
}
else
{
warning( "Weapon::SetAIRange", "unknown range: %s. Should be short, medium, long, or sniper\n", s.c_str() );
}
}
void Weapon::SetViewKick
(
Event *ev
)
{
float pitchmin, pitchmax;
float yawmin, yawmax;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
pitchmin = ev->GetFloat( 1 );
pitchmax = ev->GetFloat( 2 );
if( pitchmin <= pitchmax )
{
viewkickmin[ firemodeindex ][ 0 ] = pitchmin;
viewkickmax[ firemodeindex ][ 0 ] = pitchmax;
}
else
{
viewkickmin[ firemodeindex ][ 0 ] = pitchmax;
viewkickmax[ firemodeindex ][ 0 ] = pitchmin;
}
if( ev->NumArgs() > 2 )
{
yawmin = ev->GetFloat( 3 );
yawmax = ev->GetFloat( 4 );
if( pitchmin <= pitchmax )
{
viewkickmin[ firemodeindex ][ 1 ] = yawmin;
viewkickmax[ firemodeindex ][ 1 ] = yawmax;
}
else
{
viewkickmin[ firemodeindex ][ 1 ] = yawmax;
viewkickmax[ firemodeindex ][ 1 ] = yawmin;
}
}
}
void Weapon::SetMovementSpeed
(
Event *ev
)
{
if( g_gametype->integer )
return;
m_fMovementSpeed = ev->GetFloat( 1 );
}
void Weapon::SetDMMovementSpeed
(
Event *ev
)
{
if( !g_gametype->integer )
return;
m_fMovementSpeed = ev->GetFloat( 1 );
}
void Weapon::EventAmmoPickupSound
(
Event *ev
)
{
m_sAmmoPickupSound = ev->GetString( 1 );
}
void Weapon::EventNoAmmoSound
(
Event *ev
)
{
m_NoAmmoSound = ev->GetString( 1 );
}
float Weapon::GetBulletRange
(
firemode_t mode
)
{
return bulletrange[ mode ];
}