openmohaa/code/fgame/weapon.cpp
smallmodel 5f236d4d42
Some checks failed
CodeQL / Analyze (push) Waiting to run
Build branch / build-all (push) Failing after 1m43s
Fix projectiles having an unassigned weapon owner
This fixes weapons that shoot projectiles not being taken into account for being the preferred weapon
2025-01-05 23:57:08 +01:00

4673 lines
118 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"
#include "g_spawn.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_AddToClip
(
"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_FireDelay
(
"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_SetBulletLarge
(
"bulletlarge",
EV_DEFAULT,
"i",
"bulletType",
"Set if the bullets fired are rifle bullets(1), artillery(2) or larger tracers(3)",
EV_NORMAL
);
Event EV_Weapon_SetTracerSpeed
(
"tracerspeed",
EV_DEFAULT,
"f",
"speed",
"Scale factor of how fast a tracer should travel (valid ranges 0-2)",
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_SetBulletThroughWood
(
"throughwood",
EV_DEFAULT,
"f",
"dist",
"Sets how far the bullets can go through wood",
EV_NORMAL
);
Event EV_Weapon_SetBulletThroughMetal
(
"throughmetal",
EV_DEFAULT,
"f",
"dist",
"Sets how far the bullets can go through metal",
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",
EV_NORMAL
);
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_AddAdditionalStartAmmo
(
"additionalstartammo",
EV_DEFAULT,
"si",
"ammotype amount",
"Gives some additional start ammo of the specified type",
EV_NORMAL
);
Event EV_Weapon_AddStartItem
(
"startitem",
EV_DEFAULT,
"s",
"itemname",
"Adds an item to the starting loadout of the weapon",
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_SetSecondaryAmmoInHud
(
"secondaryammoinhud",
EV_DEFAULT,
NULL,
NULL,
"Makes the weapon show its secondary ammo to the hud.",
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_SetType
(
"weapontype",
EV_DEFAULT,
"s",
"weapon_type",
"Sets the weapon type",
EV_NORMAL
);
Event EV_Weapon_SetGroup
(
"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",
EV_NORMAL
);
Event EV_Weapon_SetSemiAuto
(
"semiauto",
EV_DEFAULT,
NULL,
NULL,
"Sets the weapon to fire semi-auto",
EV_NORMAL
);
Event EV_Weapon_AttachToHand
(
"attachtohand",
EV_DEFAULT,
"s",
"weapon_hand",
"Attaches an active weapon to the specified hand",
EV_NORMAL
);
Event EV_Weapon_CantPartialReload
(
"cantpartialreload",
EV_DEFAULT,
NULL,
NULL,
"Prevents the weapon from being reloaded part way through a clip",
EV_NORMAL
);
Event EV_Weapon_DMCantPartialReload
(
"dmcantpartialreload",
EV_DEFAULT,
NULL,
NULL,
"Prevents the weapon from being reloaded part way through a clip for DM",
EV_NORMAL
);
Event EV_Weapon_FallingAngleAdjust
(
"fallingangleadjust",
EV_DEFAULT,
0,
0,
"Adjusts the weapons angles as it falls to the ground",
EV_NORMAL
);
Event EV_Weapon_SetViewKick
(
"viewkick",
EV_DEFAULT,
"ffFF",
"pitchmin pitchmax yawmin yawmax",
"Adds kick to the view of the owner when fired.",
EV_NORMAL
);
Event EV_Weapon_MovementSpeed
(
"movementspeed",
EV_DEFAULT,
"f",
"speedmult",
"Alters the movement speed of the player when he has the weapon out",
EV_NORMAL
);
Event EV_Weapon_DMMovementSpeed
(
"dmmovementspeed",
EV_DEFAULT,
"f",
"speedmult",
"Alters the movement speed of the player when he has the weapon out",
EV_NORMAL
);
Event EV_Weapon_MaxFireMovement
(
"maxfiremovement",
EV_DEFAULT,
"f",
"speedmult",
"Sets the max speed the player can be moving to fire the weapon (fraction of weapon's running speed)",
EV_NORMAL
);
Event EV_Weapon_ZoomMovement
(
"zoommovement",
EV_DEFAULT,
"f",
"speedmult",
"Sets the max speed the player can move while zoomed (fraction of weapon's running speed)",
EV_NORMAL
);
Event EV_Weapon_AmmoPickupSound
(
"ammopickupsound",
EV_DEFAULT,
"s",
"name",
"sets the weapon's ammo pickup sound alias",
EV_NORMAL
);
Event EV_Weapon_NoAmmoSound
(
"noammosound",
EV_DEFAULT,
"s",
"name",
"sets the weapon's dry fire sound alias",
EV_NORMAL
);
Event EV_Weapon_MaxMovementSound
(
"maxmovementsound",
EV_DEFAULT,
"s",
"name",
"sets the weapon's movement fire prevention sound alias",
EV_NORMAL
);
Event EV_Weapon_NumFireAnims
(
"numfireanims",
EV_DEFAULT,
"i",
"value",
"Sets the number of fire animations this weapon uses.",
EV_NORMAL
);
Event EV_Weapon_SetCurrentFireAnim
(
"setcurrentfireanim",
EV_DEFAULT,
"i",
"value",
"Sets the current firing animation.",
EV_NORMAL
);
Event EV_Weapon_SubType
(
"weaponsubtype",
EV_DEFAULT,
"i",
"subtype",
"sets the weapon's sub-type. Used by smoke grenades.",
EV_NORMAL
);
Event EV_SetCookTime
(
"cooktime",
EV_DEFAULT,
"f",
"cooktime",
"sets weapons cook time.",
EV_NORMAL
);
Event EV_OverCooked
(
"overcooked",
EV_DEFAULT,
NULL,
NULL,
"used when the cookable weapon has been over cooked.",
EV_NORMAL
);
Event EV_OverCooked_Warning
(
"overcooked_warning",
EV_DEFAULT,
NULL,
NULL,
"causes a warning sound to play that the grenade is about to be overcooked.",
EV_NORMAL
);
//
// Added in OPM
//
Event EV_Weapon_DoneAnimating
(
"doneanimating",
EV_DEFAULT,
NULL,
NULL,
"Signals the end of animation",
EV_NORMAL
);
Event EV_Weapon_GetPutaway
(
"putaway",
EV_DEFAULT,
NULL,
NULL,
"Returns whether or not the given weapon is being put away",
EV_GETTER
);
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::FillAmmoClip },
{&EV_Weapon_EmptyClip, &Weapon::EmptyAmmoClip },
{&EV_Weapon_AddToClip, &Weapon::AddToAmmoClip },
{&EV_Weapon_SetMaxRange, &Weapon::SetMaxRangeEvent },
{&EV_Weapon_SetMinRange, &Weapon::SetMinRangeEvent },
{&EV_Weapon_FireDelay, &Weapon::SetFireDelay },
{&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_SetBulletLarge, &Weapon::SetBulletLarge },
{&EV_Weapon_SetTracerSpeed, &Weapon::SetTracerSpeed },
{&EV_Weapon_SetBulletCount, &Weapon::SetBulletCount },
{&EV_Weapon_SetBulletKnockback, &Weapon::SetBulletKnockback },
{&EV_Weapon_SetBulletThroughWood, &Weapon::SetBulletThroughWood },
{&EV_Weapon_SetBulletThroughMetal, &Weapon::SetBulletThroughMetal },
{&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_AddAdditionalStartAmmo, &Weapon::AddAdditionalStartAmmo },
{&EV_Weapon_AddStartItem, &Weapon::AddStartItem },
{&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_SetType, &Weapon::SetWeaponType },
{&EV_Weapon_SetGroup, &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::SetDMFireDelay },
{&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_MaxFireMovement, &Weapon::SetMaxFireMovement },
{&EV_Weapon_ZoomMovement, &Weapon::SetZoomMovement },
{&EV_Weapon_AmmoPickupSound, &Weapon::EventAmmoPickupSound },
{&EV_Weapon_NoAmmoSound, &Weapon::EventNoAmmoSound },
{&EV_Weapon_MaxMovementSound, &Weapon::EventMaxMovementSound },
{&EV_Weapon_NumFireAnims, &Weapon::SetNumFireAnims },
{&EV_Weapon_NumFireAnims, &Weapon::SetWeaponSubtype },
{&EV_SetCookTime, &Weapon::SetCookTime },
{&EV_OverCooked, &Weapon::OnOverCooked },
{&EV_OverCooked_Warning, &Weapon::OnOverCookedWarning },
{&EV_Weapon_SetCurrentFireAnim, &Weapon::SetCurrentFireAnim },
{&EV_Weapon_SetSecondaryAmmoInHud, &Weapon::SetSecondaryAmmoInHud },
//
// Added in OPM
//
{&EV_Weapon_DoneAnimating, &Weapon::DoneAnimating },
{&EV_Weapon_GetPutaway, &Weapon::EventGetPutaway },
{NULL, NULL }
};
//======================
//Weapon::Weapon
//======================
Weapon::Weapon()
{
entflags |= ECF_WEAPON;
mAIRange = RANGE_SHORT;
if (LoadingSavegame) {
// Archive function will setup all necessary data
return;
}
// Set the weapon class to item by default
weapon_class = WEAPON_CLASS_ITEM;
order = 0;
// Owner of the weapon
owner = NULL;
// Maximum spread multiplier while firing
m_fFireSpreadMultCap[0] = 0;
m_fFireSpreadMultCap[1] = 0;
// 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;
// Last fire state
m_fLastFireTime = 0;
m_eLastFireMode = (firemode_t)-11;
// 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));
// Weapons don't move
setMoveType(MOVETYPE_NONE);
// What type of ammo this weapon fires
INITIALIZE_WEAPONMODE_VAR(firetype, FT_NONE);
INITIALIZE_WEAPONMODE_VAR(fire_delay, 0.1f);
// Init the bullet specs
INITIALIZE_WEAPONMODE_VAR(projectilespeed, 0);
INITIALIZE_WEAPONMODE_VAR(bulletdamage, 0);
INITIALIZE_WEAPONMODE_VAR(bulletlarge, 0);
INITIALIZE_WEAPONMODE_VAR(bulletcount, 1);
INITIALIZE_WEAPONMODE_VAR(bulletrange, 4096);
INITIALIZE_WEAPONMODE_VAR(bulletknockback, 0);
INITIALIZE_WEAPONMODE_VAR(bulletthroughwood, 0);
INITIALIZE_WEAPONMODE_VAR(bulletthroughmetal, 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);
INITIALIZE_WEAPONMODE_VAR(tracerspeed, 1);
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_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_iZoom = 0;
m_bAutoZoom = false;
m_fZoomSpreadMult = 1.0f;
m_bSemiAuto = false;
// Weapons default to making noise
next_noise_time = 0;
next_noammo_time = 0;
next_maxmovement_time = 0;
// Obviously mustn't reload at first
m_bShouldReload = false;
// 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);
// Set the stats
m_iNumShotsFired = 0;
m_iNumHits = 0;
m_iNumGroinShots = 0;
m_iNumHeadShots = 0;
m_iNumLeftArmShots = 0;
m_iNumRightArmShots = 0;
m_iNumLeftLegShots = 0;
m_iNumRightLegShots = 0;
m_iNumTorsoShots = 0;
// Set the default weapon group
m_csWeaponGroup = STRING_EMPTY;
m_fMovementSpeed = 1.0f;
m_fMaxFireMovement = 1.0f;
m_fZoomMovement = 1.0f;
m_sAmmoPickupSound = "snd_pickup_";
m_NoAmmoSound = "snd_noammo";
m_sMaxMovementSound = "snd_maxmovement";
// Always has a fire animation
m_iNumFireAnims = 1;
m_iCurrentFireAnim = 0;
// Default tag to use for muzzle and special effects
m_sTagBarrel = "tag_barrel";
m_iWeaponSubtype = 0;
// Not cooking by default
m_fCookTime = 0;
m_eCookModeIndex = FIRE_PRIMARY;
// Defaults to no secondary HUD
m_bSecondaryAmmoInHud = false;
PostEvent(EV_Weapon_IdleInit, 0);
last_owner_trigger_time = 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();
if (owner) {
RemoveFromOwner();
}
entflags &= ~ECF_WEAPON;
}
//======================
//Weapon::Delete
//======================
void Weapon::Delete(void)
{
if (g_iInThinks) {
DetachGun();
}
Item::Delete();
}
//======================
//Weapon::SetRankEvent
//======================
void Weapon::SetRankEvent(Event *ev)
{
SetRank(ev->GetInteger(1), ev->GetInteger(2));
}
//======================
//Weapon::SetWeaponType
//======================
void Weapon::SetWeaponType(Event *ev)
{
weapon_class = G_WeaponClassNameToNum(ev->GetString(1));
}
//======================
//Weapon::SetWeaponGroup
//======================
void Weapon::SetWeaponGroup(Event *ev)
{
m_csWeaponGroup = ev->GetConstString(1);
}
//======================
//Weapon::SetZoom
//======================
void Weapon::SetZoom(Event *ev)
{
m_iZoom = ev->GetInteger(1);
if (ev->NumArgs() > 1) {
int autozoom = ev->GetInteger(2);
if (autozoom) {
m_bAutoZoom = qtrue;
}
}
}
//======================
//Weapon::SetSemiAuto
//======================
void Weapon::SetSemiAuto(Event *ev)
{
m_bSemiAuto = qtrue;
}
//======================
//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_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
startammo[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::SetDMStartAmmo
//======================
void Weapon::SetDMStartAmmo(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
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_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
ammorequired[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::SetDMAmmoRequired
//======================
void Weapon::SetDMAmmoRequired(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
ammorequired[firemodeindex] = ev->GetInteger(1);
}
//======================
//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) && (!owner || !owner->isClient())) {
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(vec3_t position, vec3_t vBarrelPos, vec3_t forward, vec3_t right, vec3_t up)
{
orientation_t weap_or, barrel_or, orient;
Vector delta;
Vector aim_angles;
int tagnum;
int i;
owner = (Player *)this->owner.Pointer();
// We should always have an owner
if (!owner) {
if (IsSubclassOfVehicleTurretGun()) {
VehicleTurretGun *t = (VehicleTurretGun *)this;
owner = (Player *)t->GetRemoteOwner().Pointer();
}
}
if (owner) {
tagnum = gi.Tag_NumForName(owner->edict->tiki, current_attachToTag.c_str());
// 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);
if (owner->IsSubclassOfPlayer()) {
Player *player;
// Use the player's torso angles to determine direction, but use the actual barrel to determine position
player = static_cast<Player *>(owner.Pointer());
if (forward || right || up) {
AngleVectors(player->m_vViewAng, forward, right, up);
}
VectorCopy(player->m_vViewPos, position);
if (this->GetRawTag(GetTagBarrel(), &barrel_or) && vBarrelPos) {
VectorCopy(owner->origin, vBarrelPos);
for (i = 0; i < 3; i++) {
VectorMA(vBarrelPos, orient.origin[i], owner->orientation[i], vBarrelPos);
}
MatrixMultiply(orient.axis, owner->orientation, weap_or.axis);
for (i = 0; i < 3; i++) {
VectorMA(vBarrelPos, barrel_or.origin[i], weap_or.axis[i], vBarrelPos);
}
} else if (vBarrelPos) {
VectorCopy(position, vBarrelPos);
}
} else {
VectorCopy(owner->origin, position);
for (i = 0; i < 3; i++) {
VectorMA(position, orient.origin[i], owner->orientation[i], position);
}
MatrixMultiply(orient.axis, owner->orientation, weap_or.axis);
if (this->GetRawTag(GetTagBarrel(), &barrel_or)) {
for (i = 0; i < 3; i++) {
VectorMA(position, barrel_or.origin[i], weap_or.axis[i], position);
}
}
if (vBarrelPos) {
VectorCopy(position, vBarrelPos);
}
delta = owner->GunTarget(false, position, weap_or.axis[0]) - position;
aim_angles = delta.toAngles();
if (forward || right || up) {
AngleVectors(aim_angles, forward, right, up);
}
}
} else {
if (forward || right || up) {
AngleVectors(angles, forward, right, up);
}
VectorCopy(origin, position);
if (this->GetRawTag(GetTagBarrel(), &barrel_or)) {
AnglesToAxis(angles, weap_or.axis);
for (i = 0; i < 3; i++) {
VectorMA(position, barrel_or.origin[i], weap_or.axis[i], position);
}
}
if (vBarrelPos) {
VectorCopy(position, vBarrelPos);
}
}
}
//======================
//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::SetTagBarrel
//======================
void Weapon::SetTagBarrel(const char *tagBarrel)
{
m_sTagBarrel = tagBarrel;
}
//======================
//Weapon::GetTagBarrel
//======================
str Weapon::GetTagBarrel() const
{
return m_sTagBarrel;
}
//======================
//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;
}
if (ev->NumArgs() > 1) {
SetTagBarrel(ev->GetString(2));
}
}
if (owner && owner->IsSubclassOfSentient() && owner->m_bOvercookDied) {
owner->m_bOvercookDied = false;
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, vBarrel, forward, right, up);
ApplyFireKickback(forward, 1000.0);
if (firetype[mode] != FT_LANDMINE || CanPlaceLandmine(pos, owner)) {
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;
}
switch (firetype[mode]) {
case FT_PROJECTILE:
ProjectileAttack(pos, forward, owner, projectileModel[mode], charge_fraction, 0, this);
break;
case FT_LANDMINE:
PlaceLandmine(pos, owner, projectileModel[mode], this);
break;
case FT_DEFUSE:
DefuseObject(forward, owner, bulletrange[mode]);
break;
case FT_BULLET:
{
Vector vSpread;
float fSpreadFactor;
int tracerFrequency;
SafePtr<Sentient> ownerPtr;
if (owner) {
if (owner->client) {
Player *player = (Player *)owner.Pointer();
fSpreadFactor = GetSpreadFactor(mode);
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;
}
// Don't display tracers if the owner is a player for non-turret weapons
// It would have some artifacts for the first person model, as the tracer is created from the world model
if ((g_target_game == TG_MOH || g_gametype->integer == GT_SINGLE_PLAYER) && owner && owner->IsSubclassOfPlayer()) {
if (IsSubclassOfTurretGun()) {
tracerFrequency = 3;
} else {
tracerFrequency = 0;
}
} else {
tracerFrequency = tracerfrequency[mode];
}
ownerPtr = owner;
if (!owner && IsSubclassOfVehicleTurretGun()) {
VehicleTurretGun *turretGun = static_cast<VehicleTurretGun *>(this);
ownerPtr = turretGun->GetRemoteOwner();
}
BulletAttack(
pos,
vBarrel,
forward,
right,
up,
bulletrange[mode],
bulletdamage[mode],
bulletlarge[mode],
bulletknockback[mode],
0,
GetMeansOfDeath(mode),
vSpread,
bulletcount[mode],
ownerPtr,
tracerFrequency,
&tracercount[mode],
bulletthroughwood[mode],
bulletthroughmetal[mode],
this,
tracerspeed[mode]
);
}
break;
case FT_FAKEBULLET:
{
Vector vSpread;
float fSpreadFactor;
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;
}
FakeBulletAttack(
pos,
vBarrel,
forward,
right,
up,
bulletrange[mode],
bulletdamage[mode],
bulletlarge[mode],
vSpread,
bulletcount[mode],
owner,
tracerfrequency[mode],
&tracercount[mode],
tracerspeed[mode]
);
}
break;
case FT_SPECIAL_PROJECTILE:
SpecialFireProjectile(pos, forward, right, up, owner, projectileModel[mode], charge_fraction);
break;
case FT_CLICKITEM:
ClickItemAttack(pos, forward, bulletrange[mode], owner);
break;
case 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);
}
}
break;
case FT_MELEE:
{
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++;
}
}
break;
default:
// none or unsupported
break;
}
if (!quiet[mode] && next_noise_time <= level.time) {
if (g_gametype->integer == GT_SINGLE_PLAYER) {
BroadcastAIEvent(AI_EVENT_WEAPON_FIRE, 1500);
} else {
//
// Added in OPM
// Weapon firing sounds can be heard 8000 units away
// (from ubersound.scr)
BroadcastAIEvent(AI_EVENT_WEAPON_FIRE, 8000);
}
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;
m_eLastFireMode = mode;
} else {
// Landmine weapon
if (ammo_clip_size[mode]) {
UseAmmo(-ammorequired[mode], mode);
} else if (owner && owner->isClient() && !UnlimitedAmmo(mode)) {
owner->UseAmmo(ammo_type[mode], -ammorequired[mode]);
}
}
}
//======================
//Weapon::ApplyFireKickback
//======================
void Weapon::ApplyFireKickback(const Vector& org, float kickback) {}
//======================
//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::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 true;
}
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 (m_bShareClip) {
// share the clip
mode = FIRE_PRIMARY;
}
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::ReadyToFire
//======================
qboolean Weapon::ReadyToFire(firemode_t mode, qboolean playsound)
{
if (owner && owner->IsSubclassOfSentient()) {
// Clear the cook flag
owner->m_bOvercookDied = false;
}
if (!level.playerfrozen && m_iZoom && mode == FIRE_SECONDARY) {
return qtrue;
}
// Make sure the weapon is in the ready state and the weapon has ammo
if (m_eLastFireMode != mode || level.time > (m_fLastFireTime + FireDelay(mode))) {
if (HasAmmoInClip(mode)) {
if (m_fMaxFireMovement >= 1.f) {
return qtrue;
}
if (!owner) {
return qtrue;
}
if ((owner->velocity.lengthXY() / sv_runspeed->value) <= (m_fMovementSpeed * m_fMaxFireMovement)) {
return qtrue;
}
if (playsound && (level.time > next_maxmovement_time)) {
Sound(m_sMaxMovementSound);
next_maxmovement_time = level.time + level.frametime + G_Random(0.1f) + 0.95f;
}
}
if (playsound && (level.time > next_noammo_time)) {
Sound(m_NoAmmoSound);
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]) {
// Fixed in OPM
// Always drop at least 1 because of grenades
startammo[FIRE_PRIMARY] = g_dropclips->integer * Q_max(ammo_in_clip[FIRE_PRIMARY], 1);
} else {
startammo[FIRE_PRIMARY] = g_dropclips->integer;
}
// Fixed in OPM
// Use the max between the available ammo and the ammo in clip
ammo = Q_max(AmmoAvailable(FIRE_PRIMARY), ammo_in_clip[FIRE_PRIMARY]);
if (startammo[FIRE_PRIMARY] > ammo) {
startammo[FIRE_PRIMARY] = ammo;
}
if (ammo_clip_size[FIRE_SECONDARY]) {
startammo[FIRE_SECONDARY] = g_dropclips->integer * Q_max(ammo_in_clip[FIRE_SECONDARY], 1);
} else {
startammo[FIRE_SECONDARY] = g_dropclips->integer;
}
ammo = Q_max(AmmoAvailable(FIRE_SECONDARY), ammo_in_clip[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();
if (g_droppeditemlife->value > 0) {
PostEvent(EV_Remove, g_droppeditemlife->value);
}
PostEvent(EV_Weapon_FallingAngleAdjust, level.frametime);
return true;
}
//======================
//Weapon::Charge
//======================
void Weapon::Charge(firemode_t mode)
{
if (mode == FIRE_PRIMARY) {
if (m_fCookTime > 0) {
m_eCookModeIndex = mode;
//
// Post cook event
//
PostEvent(EV_OverCooked, m_fCookTime);
PostEvent(EV_OverCooked_Warning, m_fCookTime - 1);
}
SetWeaponAnim("charge");
} else if (mode == FIRE_SECONDARY) {
if (m_fCookTime > 0) {
m_eCookModeIndex = mode;
//
// Post cook event
//
PostEvent(EV_OverCooked, m_fCookTime);
PostEvent(EV_OverCooked_Warning, m_fCookTime - 1);
}
SetWeaponAnim("secondarycharge");
}
}
//======================
//Weapon::OnOverCookedWarning
//======================
void Weapon::OnOverCookedWarning(Event *ev) {}
//======================
//Weapon::OnOverCooked
//======================
void Weapon::OnOverCooked(Event *ev)
{
if (!owner) {
return;
}
if (projectileModel[m_eCookModeIndex].length()) {
Entity *spawnedEnt;
SpawnArgs sp;
sp.setArg("model", projectileModel[m_eCookModeIndex]);
spawnedEnt = static_cast<Entity *>(sp.Spawn());
if (spawnedEnt && spawnedEnt->IsSubclassOfProjectile()) {
Event *newev;
Projectile *proj = static_cast<Projectile *>(spawnedEnt);
trace_t trace;
proj->origin = owner->origin;
proj->angles = owner->angles;
proj->owner = owner->entnum;
proj->edict->r.ownerNum = owner->entnum;
trace = G_Trace(
proj->origin,
vec_zero,
vec_zero,
owner->origin,
static_cast<Entity *>(owner.Pointer()),
owner->edict->clipmask,
qfalse,
"Weapon::OnOverCooked"
);
if (trace.ent && trace.ent->entity->entnum == world->entnum) {
proj->origin = owner->origin;
}
newev = new Event(EV_Projectile_Explode);
newev->AddEntity(NULL);
if (g_gametype->integer == GT_SINGLE_PLAYER) {
newev->AddFloat(1000.0);
}
if (owner->IsSubclassOfSentient()) {
owner->m_bOvercookDied = true;
}
proj->ProcessEvent(newev);
}
}
}
//======================
//Weapon::ReleaseFire
//======================
void Weapon::ReleaseFire(firemode_t mode, float charge_time)
{
// Make sure to stop the wepaon from cooking
CancelEventsOfType(EV_OverCooked);
CancelEventsOfType(EV_OverCooked_Warning);
// 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::GetFireAnim
//======================
const char *Weapon::GetFireAnim() const
{
if (m_iNumFireAnims > 1) {
static char tagname[256];
Com_sprintf(tagname, sizeof(tagname), "fire_%d", m_iCurrentFireAnim + 1);
return tagname;
} else {
return "fire";
}
}
//======================
//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 (m_iNumFireAnims > 1) {
m_iCurrentFireAnim++;
if (m_iCurrentFireAnim >= m_iNumFireAnims) {
m_iCurrentFireAnim = 0;
}
}
if (ammo_clip_size[FIRE_PRIMARY] && !ammo_in_clip[FIRE_PRIMARY] && HasAnim("fire_empty")) {
SetWeaponAnim("fire_empty", done_event);
} else {
SetWeaponAnim(GetFireAnim(), 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("secondaryfire_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::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_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
m_bCanPartialReload = qfalse;
}
//======================
//Weapon::SetDMCantPartialReload
//======================
void Weapon::SetDMCantPartialReload(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
m_bCanPartialReload = qfalse;
}
//======================
//Weapon::AddAdditionalStartAmmo
//======================
void Weapon::AddAdditionalStartAmmo(Event *ev)
{
m_additionalStartAmmoTypes.AddObject(ev->GetString(1));
m_additionalStartAmmoAmounts.AddObject(ev->GetInteger(2));
}
//======================
//Weapon::AddStartItem
//======================
void Weapon::AddStartItem(Event *ev)
{
m_startItems.AddObject(ev->GetString(1));
}
//======================
//Weapon::GiveStartingAmmoToOwner
//======================
void Weapon::GiveStartingAmmoToOwner(Event *ev)
{
str ammotype;
int mode;
int i;
if (!owner) {
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);
}
}
}
if (m_additionalStartAmmoTypes.NumObjects()) {
for (i = 1; i <= m_additionalStartAmmoTypes.NumObjects(); i++) {
const str& type = m_additionalStartAmmoTypes.ObjectAt(i);
int startAmmoAmount = m_additionalStartAmmoAmounts.ObjectAt(i);
if (type.length() && startAmmoAmount) {
owner->GiveAmmo(type, startAmmoAmount);
}
}
m_additionalStartAmmoTypes.ClearObjectList();
m_additionalStartAmmoAmounts.ClearObjectList();
}
if (m_startItems.NumObjects()) {
for (i = 1; i <= m_startItems.NumObjects(); i++) {
const str& itemName = m_startItems.ObjectAt(i);
if (itemName.length()) {
owner->giveItem(itemName);
}
}
m_startItems.ClearObjectList();
}
}
//======================
//Weapon::IsSecondaryWeapon
//======================
qboolean Weapon::IsSecondaryWeapon(void)
{
return (weapon_class & WEAPON_CLASS_SECONDARY);
}
//======================
//Weapon::PickupWeapon
//======================
void Weapon::PickupWeapon(Event *ev)
{
Sentient *sen;
Entity *other;
qboolean hasweapon;
qboolean hasclass;
int iGiveAmmo;
str realname;
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);
hasclass = sen->HasWeaponClass(weapon_class);
if ((g_gametype->integer || g_realismmode->integer) && !hasclass && !IsSecondaryWeapon()
&& sen->HasPrimaryWeapon()) {
// Make sure the sentient doesn't have a primary weapon on DM modes
return;
}
if ((g_gametype->integer || g_realismmode->integer) && other->IsSubclassOfPlayer()
&& (weapon_class & WEAPON_CLASS_GRENADE) && !hasweapon && hasclass) {
hasclass = false;
}
if (!hasweapon && !hasclass) {
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)) {
return;
}
setMoveType(MOVETYPE_NONE);
setSolidType(SOLID_NOT);
hideModel();
velocity = vec_zero;
avelocity = vec_zero;
CancelEventsOfType(EV_Remove);
CancelEventsOfType(EV_Weapon_FallingAngleAdjust);
DetachFromOwner();
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);
iGiveAmmo = startammo[FIRE_PRIMARY];
Sound(sPickupSound);
} else {
bool bSameAmmo[MAX_FIREMODES] = { false };
if (sen->AmmoCount(ammo_type[FIRE_PRIMARY]) == sen->MaxAmmoCount(ammo_type[FIRE_PRIMARY])) {
bSameAmmo[FIRE_PRIMARY] = true;
if (ammo_type[0] == ammo_type[1]) {
bSameAmmo[FIRE_SECONDARY] = true;
}
}
if (!bSameAmmo[FIRE_SECONDARY]) {
if (sen->AmmoCount(ammo_type[FIRE_SECONDARY]) == sen->MaxAmmoCount(ammo_type[FIRE_SECONDARY])) {
bSameAmmo[FIRE_SECONDARY] = true;
}
}
if (bSameAmmo[FIRE_PRIMARY] && bSameAmmo[FIRE_SECONDARY]) {
return;
}
if (bSameAmmo[FIRE_PRIMARY]) {
startammo[FIRE_PRIMARY] = 0;
}
if (bSameAmmo[FIRE_SECONDARY]) {
startammo[FIRE_SECONDARY] = 0;
}
iGiveAmmo = ammo_in_clip[FIRE_PRIMARY] + startammo[FIRE_PRIMARY];
if (!iGiveAmmo && !startammo[FIRE_SECONDARY]) {
// Fixed in OPM
// Don't let the sentient pick the ammo up if it already has the weapon in inventory
// and the player would get no ammo.
// This can only be picked up as a new weapon.
return;
}
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 (startammo[FIRE_PRIMARY] && ammo_type[FIRE_PRIMARY].length() && other->isClient()) {
str sMessage;
const str& sAmmoType = ammo_type[FIRE_PRIMARY];
sen->GiveAmmo(sAmmoType, iGiveAmmo);
if (!sAmmoType.icmp("grenade") || !sAmmoType.icmp("agrenade")) {
if (iGiveAmmo == 1) {
sMessage = gi.LV_ConvertString("Got 1 Grenade");
} else {
sMessage = gi.LV_ConvertString(va("Got %i Grenades", iGiveAmmo));
}
} else {
sMessage = gi.LV_ConvertString(va("Got %i %s Rounds", iGiveAmmo, sAmmoType.c_str()));
}
gi.SendServerCommand(other->edict - g_entities, "print \"" HUD_MESSAGE_YELLOW "%s\n\"", sMessage.c_str());
}
if (ammo_type[FIRE_SECONDARY] != ammo_type[FIRE_PRIMARY] && str::icmp(ammo_type[FIRE_SECONDARY], "none")) {
if (startammo[FIRE_SECONDARY] && ammo_type[FIRE_SECONDARY].length() && other->isClient()) {
str sMessage;
const str& sAmmoType = ammo_type[FIRE_SECONDARY];
iGiveAmmo = startammo[FIRE_SECONDARY];
sen->GiveAmmo(sAmmoType, iGiveAmmo);
if (!sAmmoType.icmp("grenade") || !sAmmoType.icmp("agrenade")) {
if (iGiveAmmo == 1) {
sMessage = gi.LV_ConvertString("Got 1 Grenade");
} else {
sMessage = gi.LV_ConvertString(va("Got %i Grenades", iGiveAmmo));
}
} else if (!sAmmoType.icmp("riflegrenade")) {
if (iGiveAmmo == 1) {
sMessage = gi.LV_ConvertString("Got 1 Rifle Grenade");
} else {
sMessage = gi.LV_ConvertString(va("Got %i Rifle Grenades", iGiveAmmo));
}
} 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::ForceIdle
//======================
void Weapon::ForceIdle(void)
{
SetWeaponIdleAnim();
// Force the weapon to the idle animation
weaponstate = WEAPON_READY;
// Fixed in OPM
// Stop the weapon from charging/cooking
CancelEventsOfType(EV_OverCooked);
CancelEventsOfType(EV_OverCooked_Warning);
charge_fraction = 0;
}
//======================
//Weapon::SetWeaponIdleAnim
//======================
void Weapon::SetWeaponIdleAnim(void)
{
if (m_bShareClip) {
if (ammo_clip_size[FIRE_PRIMARY] && !ammo_in_clip[FIRE_PRIMARY]) {
if (SetWeaponAnim("idle_empty")) {
return;
}
}
SetWeaponAnim("idle");
} else {
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::SetWeaponAnimEvent
//======================
void Weapon::SetWeaponAnimEvent(Event *ev)
{
//SetWeaponAnim(ev->GetString(1));
SetWeaponAnim(ev->GetString(1), EV_Weapon_DoneAnimating);
}
//======================
//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);
RestartAnimSlot(m_iAnimSlot);
int idleanim = gi.Anim_NumForName(edict->tiki, "idle");
edict->s.frameInfo[m_iAnimSlot].index = idleanim;
m_iAnimSlot = (m_iAnimSlot + 1) % MAX_WEAPON_ANIM_SLOTS;
edict->s.frameInfo[m_iAnimSlot].index = idleanim;
if (ev) {
NewAnim(animnum, ev, m_iAnimSlot);
} else {
NewAnim(animnum, m_iAnimSlot);
}
SetOnceType(m_iAnimSlot);
RestartAnimSlot(m_iAnimSlot);
return qtrue;
}
//======================
//Weapon::SetWeaponAnim
//======================
qboolean Weapon::SetWeaponAnim(const char *anim, Event& ev)
{
Event *event = new Event(ev);
return SetWeaponAnim(anim, event);
}
//======================
//Weapon::StopWeaponAnim
//======================
void Weapon::StopWeaponAnim(void)
{
int animnum;
RestartAnimSlot(m_iAnimSlot);
StopAnimating(m_iAnimSlot);
animnum = gi.Anim_NumForName(edict->tiki, "idle");
StartAnimSlot(m_iAnimSlot, animnum, 1.f);
m_iAnimSlot = (m_iAnimSlot + 1) % MAX_WEAPON_ANIM_SLOTS;
}
//======================
//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::FillAmmoClip
//======================
void Weapon::FillAmmoClip(Event *ev)
{
int amount;
int amount_used;
if (!ammo_clip_size[0] || !owner) {
return;
}
if (UnlimitedAmmo(FIRE_PRIMARY)) {
// reload directly
ammo_in_clip[FIRE_PRIMARY] = ammo_clip_size[0];
} else {
// Calc the amount the clip should get
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
ammo_in_clip[FIRE_PRIMARY] = amount_used + ammo_in_clip[FIRE_PRIMARY];
}
owner->AmmoAmountInClipChanged(ammo_type[FIRE_PRIMARY], ammo_in_clip[FIRE_PRIMARY]);
SetShouldReload(qfalse);
}
//======================
//Weapon::EmptyAmmoClip
//======================
void Weapon::EmptyAmmoClip(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]);
}
//======================
//Weapon::AddToAmmoClip
//======================
void Weapon::AddToAmmoClip(Event *ev)
{
int amount;
int amount_used;
if (!ammo_clip_size[FIRE_PRIMARY] || !owner) {
return;
}
// Calc the amount the clip should get
amount = ev->GetInteger(1);
if (amount > ammo_clip_size[FIRE_PRIMARY] - ammo_in_clip[FIRE_PRIMARY]) {
amount = ammo_clip_size[FIRE_PRIMARY] - ammo_in_clip[FIRE_PRIMARY];
}
if (UnlimitedAmmo(FIRE_PRIMARY)) {
// Stick it in the clip
ammo_in_clip[FIRE_PRIMARY] = amount + ammo_in_clip[FIRE_PRIMARY];
} else {
// use up the ammo from the player
amount_used = owner->UseAmmo(ammo_type[FIRE_PRIMARY], amount);
// Stick it in the clip
ammo_in_clip[FIRE_PRIMARY] = amount_used + ammo_in_clip[FIRE_PRIMARY];
}
owner->AmmoAmountInClipChanged(ammo_type[FIRE_PRIMARY], ammo_in_clip[FIRE_PRIMARY]);
SetShouldReload(qfalse);
}
//======================
//Weapon::SetIdleState
//======================
void Weapon::SetIdleState(int state)
{
if (!owner) {
return;
}
switch (state) {
case 0:
default:
SetWeaponAnim("idle0");
break;
case 1:
SetWeaponAnim("idle1");
break;
case 2:
SetWeaponAnim("idle2");
break;
}
}
//======================
//Weapon::StartReloading
//======================
void Weapon::StartReloading(void)
{
if (!ammo_clip_size[0] || !owner) {
return;
}
if (SetWeaponAnim("reload", EV_Weapon_DoneReloading)) {
weaponstate = WEAPON_RELOADING;
} else {
ProcessEvent(EV_Weapon_FillClip);
ProcessEvent(EV_Weapon_DoneReloading);
}
m_fFireSpreadMult[FIRE_PRIMARY] = 0;
}
//======================
//Weapon::DoneReloading
//======================
void Weapon::DoneReloading(Event *ev)
{
SetShouldReload(qfalse);
weaponstate = WEAPON_READY;
// Added in OPM
// Set to idle when done reloading
SetWeaponIdleAnim();
}
//======================
//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::SetShouldReload
//======================
void Weapon::SetShouldReload(qboolean should_reload)
{
m_bShouldReload = should_reload;
}
//======================
//Weapon::ShouldReload
//======================
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;
}
//======================
//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)
{
// 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) {
owner->ProcessEvent(*ev);
} 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;
// 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::SetFireDelay
//======================
void Weapon::SetFireDelay(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
fire_delay[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetDMFireDelay
//======================
void Weapon::SetDMFireDelay(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
fire_delay[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::FireDelay
//======================
float Weapon::FireDelay(firemode_t mode)
{
return fire_delay[mode];
}
//======================
//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("landmine")) {
firetype[firemodeindex] = FT_LANDMINE;
} else if (!ftype.icmp("defuse")) {
firetype[firemodeindex] = FT_DEFUSE;
} 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_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
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 == GT_SINGLE_PLAYER) {
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::SetBulletLarge
//======================
void Weapon::SetBulletLarge(Event *ev)
{
bulletlarge[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::SetBulletLarge
//======================
void Weapon::SetTracerSpeed(Event *ev)
{
tracerspeed[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetDMBulletDamage
//======================
void Weapon::SetDMBulletDamage(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
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 == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletknockback[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletThroughWood
//======================
void Weapon::SetBulletThroughWood(Event *ev)
{
bulletthroughwood[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletThroughMetal
//======================
void Weapon::SetBulletThroughMetal(Event *ev)
{
bulletthroughmetal[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletRange
//======================
void Weapon::SetBulletRange(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletrange[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetDMBulletRange
//======================
void Weapon::SetDMBulletRange(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
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_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletcount[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetDMBulletCount
//======================
void Weapon::SetDMBulletCount(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletcount[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletSpread
//======================
void Weapon::SetBulletSpread(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
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 == GT_SINGLE_PLAYER) {
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_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
m_fZoomSpreadMult = ev->GetFloat(1);
}
//======================
//Weapon::SetDMZoomSpreadMult
//======================
void Weapon::SetDMZoomSpreadMult(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
m_fZoomSpreadMult = ev->GetFloat(1);
}
//======================
//Weapon::SetZoomSpreadMult
//======================
void Weapon::SetFireSpreadMult(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
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 == GT_SINGLE_PLAYER) {
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::Secondary
//======================
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 == GT_SINGLE_PLAYER) {
return;
}
crosshair = ev->GetBoolean(1);
}
//====================
//Weapon::MainAttachToTag
//====================
void Weapon::MainAttachToTag(Event *ev)
{
attachToTag_main = ev->GetString(1);
}
//====================
//Weapon::OffHandAttachToTag
//====================
void Weapon::OffHandAttachToTag(Event *ev)
{
attachToTag_offhand = 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::SetMeansOfDeath
//======================
void Weapon::SetMeansOfDeath(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
meansofdeath[firemodeindex] = (meansOfDeath_t)MOD_string_to_int(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;
}
//======================
//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);
}
//======================
//Weapon::MakeNoise
//======================
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;
}
}
//======================
//Weapon::ShowInfo
//======================
void Weapon::ShowInfo(float fDot, float fDist) {}
//======================
//Weapon::FallingAngleAdjust
//======================
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);
}
//======================
//Weapon::GetUseCrosshair
//======================
qboolean Weapon::GetUseCrosshair() const
{
if (g_protocol < protocol_e::PROTOCOL_MOHTA_MIN) {
// Always show the crosshair in 1.11 and below
return qtrue;
}
if (m_fMaxFireMovement >= 1.f) {
return crosshair;
}
if (!owner || (owner->velocity.lengthXY() / sv_runspeed->value) <= (m_fMaxFireMovement * m_fMovementSpeed)) {
return crosshair;
}
return qfalse;
}
//======================
//Weapon::SetAIRange
//======================
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());
}
}
//======================
//Weapon::SetViewKick
//======================
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;
}
}
}
//======================
//Weapon::SetMovementSpeed
//======================
void Weapon::SetMovementSpeed(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
m_fMovementSpeed = ev->GetFloat(1);
}
//======================
//Weapon::SetDMMovementSpeed
//======================
void Weapon::SetDMMovementSpeed(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
m_fMovementSpeed = ev->GetFloat(1);
}
//======================
//Weapon::SetMaxFireMovement
//======================
void Weapon::SetMaxFireMovement(Event *ev)
{
m_fMaxFireMovement = ev->GetFloat(1);
if (m_fMaxFireMovement > 1.0) {
// Cap at 1.0
m_fMaxFireMovement = 1.0;
}
}
//======================
//Weapon::SetZoomMovement
//======================
void Weapon::SetZoomMovement(Event *ev)
{
m_fZoomMovement = ev->GetFloat(1);
if (m_fZoomMovement > 1.0) {
// Cap at 1.0
m_fZoomMovement = 1.0;
}
}
//======================
//Weapon::EventAmmoPickupSound
//======================
void Weapon::EventAmmoPickupSound(Event *ev)
{
m_sAmmoPickupSound = ev->GetString(1);
}
//======================
//Weapon::EventNoAmmoSound
//======================
void Weapon::EventNoAmmoSound(Event *ev)
{
m_NoAmmoSound = ev->GetString(1);
}
//======================
//Weapon::EventMaxMovementSound
//======================
void Weapon::EventMaxMovementSound(Event *ev)
{
m_sMaxMovementSound = ev->GetString(1);
}
//======================
//Weapon::SetNumFireAnims
//======================
void Weapon::SetNumFireAnims(Event *ev)
{
m_iNumFireAnims = ev->GetInteger(1);
}
//======================
//Weapon::SetWeaponSubtype
//======================
void Weapon::SetWeaponSubtype(Event *ev)
{
m_iWeaponSubtype = ev->GetInteger(1);
}
//======================
//Weapon::SetCookTime
//======================
void Weapon::SetCookTime(Event *ev)
{
m_fCookTime = ev->GetFloat(1);
}
//======================
//Weapon::SetCurrentFireAnim
//======================
void Weapon::SetCurrentFireAnim(Event *ev)
{
m_iCurrentFireAnim = ev->GetInteger(1);
}
//======================
//Weapon::SetSecondaryAmmoInHud
//======================
void Weapon::SetSecondaryAmmoInHud(Event *ev)
{
m_bSecondaryAmmoInHud = qtrue;
}
//======================
//Weapon::SetRank
//======================
void Weapon::SetRank(int order, int rank)
{
this->order = order;
this->rank = rank;
}
//======================
//Weapon::GetRank
//======================
int Weapon::GetRank(void)
{
return rank;
}
//======================
//Weapon::GetOrder
//======================
int Weapon::GetOrder(void)
{
return order;
}
//======================
//Weapon::GetWeaponClass
//======================
int Weapon::GetWeaponClass(void) const
{
return weapon_class;
}
//======================
//Weapon::GetWeaponGroup
//======================
const_str Weapon::GetWeaponGroup(void) const
{
return m_csWeaponGroup;
};
//======================
//Weapon::SetItemSlot
//======================
void Weapon::SetItemSlot(int slot)
{
weapon_class &= ~WEAPON_CLASS_ITEM_SLOT_BITS;
weapon_class |= slot & WEAPON_CLASS_ITEM_SLOT_BITS;
}
//======================
//Weapon::GetPutaway
//======================
qboolean Weapon::GetPutaway(void)
{
return putaway;
}
//======================
//Weapon::SetPutAway
//======================
void Weapon::SetPutAway(qboolean p)
{
putaway = p;
}
//======================
//Weapon::GetState
//======================
weaponstate_t Weapon::GetState(void)
{
return weaponstate;
}
//======================
//Weapon::SpecialFireProjectile
//======================
void Weapon::SpecialFireProjectile(
Vector pos, Vector forward, Vector right, Vector up, Entity *owner, str projectileModel, float charge_fraction
)
{
// Does nothing
}
//======================
//Weapon::GetCurrentAttachToTag
//======================
str Weapon::GetCurrentAttachToTag(void)
{
return current_attachToTag;
}
//======================
//Weapon::SetCurrentAttachToTag
//======================
void Weapon::SetCurrentAttachToTag(str s)
{
current_attachToTag = s;
}
//======================
//Weapon::GetHolsterTag
//======================
str Weapon::GetHolsterTag(void)
{
return holster_attachToTag;
}
//======================
//Weapon::GetUseNoAmmo
//======================
qboolean Weapon::GetUseNoAmmo(void)
{
return use_no_ammo;
}
//====================
//Weapon::GetZoom
//====================
int Weapon::GetZoom(void)
{
return m_iZoom;
}
//====================
//Weapon::GetAutoZoom
//====================
qboolean Weapon::GetAutoZoom(void)
{
return m_bAutoZoom;
}
//======================
//Weapon::GetMovementSpeed
//======================
float Weapon::GetMovementSpeed() const
{
return m_fMovementSpeed;
}
//======================
//Weapon::IsCarryableTurret
//======================
bool Weapon::IsCarryableTurret() const
{
return false;
}
//======================
//Weapon::GetStartAmmo
//======================
int Weapon::GetStartAmmo(firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
return startammo[mode];
}
//======================
//Weapon::IsSemiAuto
//======================
qboolean Weapon::IsSemiAuto(void)
{
return m_bSemiAuto;
}
//======================
//Weapon::ForceState
//======================
void Weapon::ForceState(weaponstate_t state)
{
weaponstate = state;
}
//======================
//Weapon::MuzzleClear
//======================
qboolean Weapon::MuzzleClear(void)
{
return qtrue;
}
//======================
//Weapon::GetBulletRange
//======================
float Weapon::GetBulletRange(firemode_t mode)
{
return bulletrange[mode];
}
//======================
//Weapon::GetSpreadFactor
//======================
float Weapon::GetSpreadFactor(firemode_t mode)
{
float fSpreadFactor;
if (owner && owner->client) {
Player* player = (Player*)owner.Pointer();
fSpreadFactor = player->velocity.length() / sv_runspeed->integer;
if (fSpreadFactor > 1.0f) {
fSpreadFactor = 1.0f;
}
} else {
fSpreadFactor = 0.5f;
}
return fSpreadFactor;
}
//======================
//Weapon::GetChargeFraction
//======================
float Weapon::GetChargeFraction(void) const
{
return charge_fraction;
}
//======================
//Weapon::GetScriptOwner
//======================
Listener *Weapon::GetScriptOwner(void)
{
return owner;
}
//======================
//Weapon::DoneAnimating
//======================
void Weapon::DoneAnimating(Event* ev) {
// Added in OPM
// Set to idle when done reloading
SetWeaponIdleAnim();
}
//======================
//Weapon::GetMaxFireMovementMult
//======================
void Weapon::EventGetPutaway(Event* ev) {
// Added in OPM
// Set to idle when done reloading
ev->AddInteger(GetPutaway());
}
//======================
//Weapon::GetMaxFireMovementMult
//======================
float Weapon::GetMaxFireMovementMult() const {
return m_fMaxFireMovement * m_fMovementSpeed;
}
//======================
//Weapon::GetMaxFireMovementMult
//======================
float Weapon::GetZoomMovement() const {
return m_fZoomMovement * m_fMovementSpeed;
}
//======================
//Weapon::GetMaxFireMovement
//======================
float Weapon::GetMaxFireMovement() const {
return m_fMaxFireMovement;
}