openmohaa/code/fgame/weapon.cpp

5125 lines
105 KiB
C++
Raw Normal View History

2016-03-27 11:49:47 +02:00
/*
===========================================================================
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"
2023-04-29 21:56:38 +02:00
#include "g_phys.h"
2016-03-27 11:49:47 +02:00
#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"
2023-04-29 21:56:38 +02:00
#include "debuglines.h"
2023-07-31 23:41:53 +02:00
#include "g_spawn.h"
2016-03-27 11:49:47 +02:00
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
2016-03-27 11:49:47 +02:00
(
"clip_add",
EV_DEFAULT,
"i",
"ammoAmount",
"Add to the weapons ammo clip with ammo from its owner",
EV_NORMAL
);
Event EV_Weapon_SetMaxRange
(
"maxrange",
EV_DEFAULT,
"f",
"maxRange",
"Set the maximum range of a weapon so the AI knows how to use it",
EV_NORMAL
);
Event EV_Weapon_SetMinRange
(
"minrange",
EV_DEFAULT,
"f",
"minRange",
"Set the minimum range of a weapon so the AI knows how to use it",
EV_NORMAL
);
Event EV_Weapon_SetFireDelay
(
"firedelay",
EV_DEFAULT,
"f",
"fFireDelay",
"Set the minimum time between shots from the weapon",
EV_NORMAL
);
Event EV_Weapon_DMSetFireDelay
(
"dmfiredelay",
EV_DEFAULT,
"f",
"fFireDelay",
"Set the minimum time between shots from the weapon",
EV_NORMAL
);
Event EV_Weapon_NotDroppable
(
"notdroppable",
EV_DEFAULT,
NULL,
NULL,
"Makes a weapon not droppable"
);
Event EV_Weapon_SetAimAnim
(
"setaimanim",
EV_DEFAULT,
"si",
"aimAnimation aimFrame",
"Set the aim animation and frame for when a weapon fires",
EV_NORMAL
);
Event EV_Weapon_SetRank
(
"rank",
EV_DEFAULT,
"ii",
"iOrder iRank",
"Set the order value and power ranking for the weapon"
);
Event EV_Weapon_SetFireType
(
"firetype",
EV_DEFAULT,
"s",
"firingType",
"Set the firing type of the weapon (projectile or bullet)",
EV_NORMAL
);
Event EV_Weapon_SetAIRange
(
"airange",
EV_DEFAULT,
"s",
"airange",
"Set the range of this gun for the ai: short, medium, long, sniper",
EV_NORMAL
);
Event EV_Weapon_SetProjectile
(
"projectile",
EV_DEFAULT,
"s",
"projectileModel",
"Set the model of the projectile that this weapon fires",
EV_NORMAL
);
Event EV_Weapon_SetDMProjectile
(
"dmprojectile",
EV_DEFAULT,
"s",
"projectileModel",
"Set the model of the projectile that this weapon fires",
EV_NORMAL
);
Event EV_Weapon_SetBulletDamage
(
"bulletdamage",
EV_DEFAULT,
"f",
"bulletDamage",
"Set the damage that the bullet causes",
EV_NORMAL
);
Event EV_Weapon_SetDMBulletDamage
(
"dmbulletdamage",
EV_DEFAULT,
"f",
"bulletDamage",
"Set the damage that the bullet causes",
EV_NORMAL
);
Event EV_Weapon_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
);
2016-03-27 11:49:47 +02:00
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
);
2016-03-27 11:49:47 +02:00
Event EV_Weapon_SetBulletCount
(
"bulletcount",
EV_DEFAULT,
"f",
"bulletCount",
"Set the number of bullets this weapon shoots when fired",
EV_NORMAL
);
Event EV_Weapon_SetDMBulletCount
(
"dmbulletcount",
EV_DEFAULT,
"f",
"bulletCount",
"Set the number of bullets this weapon shoots when fired",
EV_NORMAL
);
Event EV_Weapon_SetBulletRange
(
"bulletrange",
EV_DEFAULT,
"f",
"bulletRange",
"Set the range of the bullets",
EV_NORMAL
);
Event EV_Weapon_SetDMBulletRange
(
"dmbulletrange",
EV_DEFAULT,
"f",
"bulletRange",
"Set the range of the bullets",
EV_NORMAL
);
Event EV_Weapon_SetBulletSpread
(
"bulletspread",
EV_DEFAULT,
"ffFF",
"bulletSpreadX bulletSpreadY bulletSpreadXmax bulletSpreadYmax",
"Set the min & optional max spread of the bullet in the x and y axis",
EV_NORMAL
);
Event EV_Weapon_SetDMBulletSpread
(
"dmbulletspread",
EV_DEFAULT,
"ffFF",
"bulletSpreadX bulletSpreadY bulletSpreadXmax bulletSpreadYmax",
"Set the min & optional max spread of the bullet in the x and y axis",
EV_NORMAL
);
Event EV_Weapon_SetZoomSpreadMult
(
"zoomspreadmult",
EV_DEFAULT,
"f",
"scale",
"Sets the spread multiplier for when using the zoom on a zooming weapon",
EV_NORMAL
);
Event EV_Weapon_SetDMZoomSpreadMult
(
"dmzoomspreadmult",
EV_DEFAULT,
"f",
"scale",
"Sets the spread multiplier for when using the zoom on a zooming weapon",
EV_NORMAL
);
Event EV_Weapon_SetFireSpreadMult
(
"firespreadmult",
EV_DEFAULT,
"ffff",
"scaleadd falloff cap maxtime",
"Sets a time decayed multiplyer to spread when the weapon is fired",
EV_NORMAL
);
Event EV_Weapon_SetDMFireSpreadMult
(
"dmfirespreadmult",
EV_DEFAULT,
"ffff",
"scaleadd falloff cap maxtime",
"Sets a time decayed multiplyer to spread when the weapon is fired",
EV_NORMAL
);
Event EV_Weapon_SetTracerFrequency
(
"tracerfrequency",
EV_DEFAULT,
"f",
"frequenct",
"Set the frequency of making tracers"
);
Event EV_Weapon_SetRange
(
"range",
EV_DEFAULT,
"f",
"range",
"Set the range of the weapon",
EV_NORMAL
);
Event EV_Weapon_Secondary
(
"secondary",
EV_DEFAULT,
"SSSSSSSS",
"arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8",
"Set the secondary mode of the weapon, by passing commands through",
EV_NORMAL
);
Event EV_Weapon_AmmoType
(
"ammotype",
EV_DEFAULT,
"s",
"name",
"Set the type of ammo this weapon uses",
EV_NORMAL
);
Event EV_Weapon_StartAmmo
(
"startammo",
EV_DEFAULT,
"i",
"amount",
"Set the starting ammo of this weapon",
EV_NORMAL
);
Event EV_Weapon_DMStartAmmo
(
"dmstartammo",
EV_DEFAULT,
"i",
"amount",
"Set the starting ammo of this weapon",
EV_NORMAL
);
Event EV_Weapon_AmmoRequired
(
"ammorequired",
EV_DEFAULT,
"i",
"amount",
"Set the amount of ammo this weapon requires to fire",
EV_NORMAL
);
Event EV_Weapon_DMAmmoRequired
(
"dmammorequired",
EV_DEFAULT,
"i",
"amount",
"Set the amount of ammo this weapon requires to fire",
EV_NORMAL
);
Event EV_Weapon_MaxChargeTime
(
"maxchargetime",
EV_DEFAULT,
"i",
"time",
"Set the maximum time the weapon may be charged up",
EV_NORMAL
);
Event EV_Weapon_MinChargeTime
(
"minchargetime",
EV_DEFAULT,
"i",
"time",
"Set the minimum time the weapon must be charged up",
EV_NORMAL
);
Event EV_Weapon_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
);
2016-03-27 11:49:47 +02:00
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",
2016-03-27 11:49:47 +02:00
EV_NORMAL
);
2016-03-27 11:49:47 +02:00
Event EV_Weapon_SetQuiet
(
"quiet",
EV_DEFAULT,
NULL,
NULL,
"Makes the weapon make no noise.",
2016-03-27 11:49:47 +02:00
EV_NORMAL
);
Event EV_Weapon_SetSecondaryAmmoInHud
(
"secondaryammoinhud",
EV_DEFAULT,
NULL,
NULL,
"Makes the weapon show its secondary ammo to the hud.",
EV_NORMAL
);
2016-03-27 11:49:47 +02:00
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
2016-03-27 11:49:47 +02:00
(
"weapontype",
EV_DEFAULT,
"s",
"weapon_type",
"Sets the weapon type",
EV_NORMAL
);
Event EV_Weapon_SetGroup
2016-03-27 11:49:47 +02:00
(
"weapongroup",
EV_DEFAULT,
"s",
"weapon_group",
"Sets the weapon group, a set of animations for actor animations scripts to use",
EV_NORMAL
);
Event EV_Weapon_SetZoom
(
"zoom",
EV_DEFAULT,
"iI",
"zoomfov autozoom",
"Sets fov to zoom to on a secondary fire"
);
Event EV_Weapon_SetSemiAuto
(
"semiauto",
EV_DEFAULT,
NULL,
NULL,
"Sets the weapon to fire semi-auto"
);
Event EV_Weapon_AttachToHand
(
"attachtohand",
EV_DEFAULT,
"s",
"weapon_hand",
"Attaches an active weapon to the specified hand"
);
Event EV_Weapon_CantPartialReload
(
"cantpartialreload",
EV_DEFAULT,
NULL,
NULL,
"Prevents the weapon from being reloaded part way through a clip"
);
Event EV_Weapon_DMCantPartialReload
(
"dmcantpartialreload",
EV_DEFAULT,
NULL,
NULL,
"Prevents the weapon from being reloaded part way through a clip for DM"
);
Event EV_Weapon_FallingAngleAdjust
(
"fallingangleadjust",
EV_DEFAULT,
0,
0,
"Adjusts the weapons angles as it falls to the ground"
);
Event EV_Weapon_SetViewKick
(
"viewkick",
EV_DEFAULT,
"ffFF",
"pitchmin pitchmax yawmin yawmax",
"Adds kick to the view of the owner when fired."
);
Event EV_Weapon_MovementSpeed
(
"movementspeed",
EV_DEFAULT,
"f",
"speedmult",
"Alters the movement speed of the player when he has the weapon out"
);
Event EV_Weapon_DMMovementSpeed
(
"dmmovementspeed",
EV_DEFAULT,
"f",
"speedmult",
"Alters the movement speed of the player when he has the weapon out"
);
Event EV_Weapon_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)"
);
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)"
);
2016-03-27 11:49:47 +02:00
Event EV_Weapon_AmmoPickupSound
(
"ammopickupsound",
EV_DEFAULT,
"s",
"name",
"sets the weapon's ammo pickup sound alias"
);
Event EV_Weapon_NoAmmoSound
(
"noammosound",
EV_DEFAULT,
"s",
"name",
"sets the weapon's dry fire sound alias"
);
Event EV_Weapon_MaxMovementSound
(
"maxmovementsound",
EV_DEFAULT,
"s",
"name",
"sets the weapon's movement fire prevention sound alias"
);
Event EV_Weapon_NumFireAnims
(
"numfireanims",
EV_DEFAULT,
"i",
"value",
"Sets the number of fire animations this weapon uses."
);
Event EV_Weapon_SetCurrentFireAnim
(
"setcurrentfireanim",
EV_DEFAULT,
"i",
"value",
"Sets the current firing animation."
);
Event EV_Weapon_SubType
(
"weaponsubtype",
EV_DEFAULT,
"i",
"subtype",
"sets the weapon's sub-type. Used by smoke grenades."
);
Event EV_SetCookTime
(
"cooktime",
EV_DEFAULT,
"f",
"cooktime",
"sets weapons cook time."
);
Event EV_OverCooked
(
"overcooked",
EV_DEFAULT,
NULL,
NULL,
"used when the cookable weapon has been over cooked."
);
Event EV_OverCooked_Warning
(
"overcooked_warning",
EV_DEFAULT,
NULL,
NULL,
"causes a warning sound to play that the grenade is about to be overcooked."
);
2016-03-27 11:49:47 +02:00
CLASS_DECLARATION( Item, Weapon, NULL )
{
{ &EV_SetAnim, &Weapon::SetWeaponAnimEvent },
{ &EV_Item_Pickup, &Weapon::PickupWeapon },
{ &EV_Weapon_DoneRaising, &Weapon::DoneRaising },
{ &EV_Weapon_DoneFiring, &Weapon::DoneFiring },
{ &EV_Weapon_Idle, &Weapon::Idle },
{ &EV_Weapon_IdleInit, &Weapon::IdleInit },
{ &EV_BroadcastAIEvent, &Weapon::WeaponSound },
{ &EV_Weapon_DoneReloading, &Weapon::DoneReloading },
{ &EV_Weapon_SetAmmoClipSize, &Weapon::SetAmmoClipSize },
{ &EV_Weapon_SetAmmoInClip, &Weapon::SetAmmoInClip },
{ &EV_Weapon_SetShareClip, &Weapon::SetShareClip },
{ &EV_Weapon_FillClip, &Weapon::EventClipFill },
{ &EV_Weapon_EmptyClip, &Weapon::EventClipEmpty },
{ &EV_Weapon_AddToClip, &Weapon::EventClipAdd },
2016-03-27 11:49:47 +02:00
{ &EV_Weapon_SetMaxRange, &Weapon::SetMaxRangeEvent },
{ &EV_Weapon_SetMinRange, &Weapon::SetMinRangeEvent },
{ &EV_Weapon_SetFireDelay, &Weapon::EventSetFireDelay },
{ &EV_Weapon_NotDroppable, &Weapon::NotDroppableEvent },
{ &EV_Weapon_SetAimAnim, &Weapon::SetAimAnim },
{ &EV_Weapon_Shoot, &Weapon::Shoot },
{ &EV_Weapon_SetRank, &Weapon::SetRankEvent },
{ &EV_Weapon_SetFireType, &Weapon::SetFireType },
{ &EV_Weapon_SetAIRange, &Weapon::SetAIRange },
{ &EV_Weapon_SetProjectile, &Weapon::SetProjectile },
{ &EV_Weapon_SetDMProjectile, &Weapon::SetDMProjectile },
{ &EV_Weapon_SetBulletDamage, &Weapon::SetBulletDamage },
{ &EV_Weapon_SetBulletLarge, &Weapon::SetBulletLarge },
{ &EV_Weapon_SetTracerSpeed, &Weapon::SetTracerSpeed },
2016-03-27 11:49:47 +02:00
{ &EV_Weapon_SetBulletCount, &Weapon::SetBulletCount },
{ &EV_Weapon_SetBulletKnockback, &Weapon::SetBulletKnockback },
{ &EV_Weapon_SetBulletThroughWood, &Weapon::SetBulletThroughWood },
{ &EV_Weapon_SetBulletThroughMetal, &Weapon::SetBulletThroughMetal },
2016-03-27 11:49:47 +02:00
{ &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 },
2016-03-27 11:49:47 +02:00
{ &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 },
2016-03-27 11:49:47 +02:00
{ &EV_Weapon_SetZoom, &Weapon::SetZoom },
{ &EV_Weapon_SetSemiAuto, &Weapon::SetSemiAuto },
{ &EV_Weapon_AttachToHand, &Weapon::AttachToHand },
{ &EV_Weapon_CantPartialReload, &Weapon::SetCantPartialReload },
{ &EV_Weapon_FallingAngleAdjust, &Weapon::FallingAngleAdjust },
{ &EV_Weapon_SetViewKick, &Weapon::SetViewKick },
{ &EV_Weapon_DMSetFireDelay, &Weapon::EventSetDMFireDelay },
{ &EV_Weapon_SetDMBulletDamage, &Weapon::SetDMBulletDamage },
{ &EV_Weapon_SetDMBulletCount, &Weapon::SetDMBulletCount },
{ &EV_Weapon_SetDMBulletSpread, &Weapon::SetDMBulletSpread },
{ &EV_Weapon_SetZoomSpreadMult, &Weapon::SetZoomSpreadMult },
{ &EV_Weapon_SetDMZoomSpreadMult, &Weapon::SetDMZoomSpreadMult },
{ &EV_Weapon_SetFireSpreadMult, &Weapon::SetFireSpreadMult },
{ &EV_Weapon_SetDMFireSpreadMult, &Weapon::SetDMFireSpreadMult },
{ &EV_Weapon_DMAmmoRequired, &Weapon::SetDMAmmoRequired },
{ &EV_Weapon_DMCantPartialReload, &Weapon::SetDMCantPartialReload },
{ &EV_Weapon_DMStartAmmo, &Weapon::SetDMStartAmmo },
{ &EV_Weapon_SetDMBulletRange, &Weapon::SetDMBulletRange },
{ &EV_Weapon_DMCrosshair, &Weapon::DMCrosshair },
{ &EV_Weapon_MovementSpeed, &Weapon::SetMovementSpeed },
{ &EV_Weapon_DMMovementSpeed, &Weapon::SetDMMovementSpeed },
{ &EV_Weapon_MaxFireMovement, &Weapon::SetMaxFireMovement },
{ &EV_Weapon_ZoomMovement, &Weapon::SetZoomMovement },
2016-03-27 11:49:47 +02:00
{ &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 },
2016-03-27 11:49:47 +02:00
{ NULL, NULL }
};
//======================
//Weapon::Weapon
//======================
Weapon::Weapon()
{
entflags |= EF_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;
2016-03-27 11:49:47 +02:00
// Owner of the weapon
owner = NULL;
// Maximum spread multiplier while firing
m_fFireSpreadMultCap[0] = 0;
m_fFireSpreadMultCap[1] = 0;
2016-03-27 11:49:47 +02:00
// 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;
2016-03-27 11:49:47 +02:00
// The initial state of the weapon
weaponstate = WEAPON_HOLSTERED;
// Is the weapon droppable when the owner is killed
notdroppable = false;
// Aim animation for behavior of monsters
aimanim = -1;
aimframe = 0;
m_iAnimSlot = 0;
// start off unattached
attached = false;
// maximum effective firing distance (for autoaim)
maxrange = 1000;
// minimum safe firing distance (for AI)
minrange = 0;
// speed of the projectile (0 == infinite speed)
memset( projectilespeed, 0, sizeof( projectilespeed ) );
// default action_level_increment
INITIALIZE_WEAPONMODE_VAR( action_level_increment, 2 );
// Weapons don't move
setMoveType( MOVETYPE_NONE );
// What type of ammo this weapon fires
INITIALIZE_WEAPONMODE_VAR( firetype, FT_NONE );
2016-03-27 11:49:47 +02:00
2023-07-31 23:41:53 +02:00
INITIALIZE_WEAPONMODE_VAR( fire_delay, 0.1f );
2016-03-27 11:49:47 +02:00
// Init the bullet specs
INITIALIZE_WEAPONMODE_VAR( projectilespeed, 0 );
INITIALIZE_WEAPONMODE_VAR( bulletdamage, 0 );
INITIALIZE_WEAPONMODE_VAR( bulletlarge, 0 );
2016-03-27 11:49:47 +02:00
INITIALIZE_WEAPONMODE_VAR( bulletcount, 1 );
INITIALIZE_WEAPONMODE_VAR( bulletrange, 4096 );
2016-03-27 11:49:47 +02:00
INITIALIZE_WEAPONMODE_VAR( bulletknockback, 0 );
INITIALIZE_WEAPONMODE_VAR( bulletthroughwood, 0 );
INITIALIZE_WEAPONMODE_VAR( bulletthroughmetal, 0 );
2016-03-27 11:49:47 +02:00
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, 0 );
2016-03-27 11:49:47 +02:00
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;
2016-03-27 11:49:47 +02:00
// 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;
2016-03-27 11:49:47 +02:00
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;
2016-03-27 11:49:47 +02:00
// 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_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;
2016-03-27 11:49:47 +02:00
PostEvent( EV_Weapon_IdleInit, 0 );
last_owner_trigger_time = 0;
2016-03-27 11:49:47 +02:00
}
//======================
//Weapon::Weapon
//======================
Weapon::Weapon
(
const char *file
)
{
// The tik file holds all the information available for a weapon
Weapon();
}
//======================
//Weapon::~Weapon
//======================
Weapon::~Weapon()
{
DetachGun();
entflags &= ~EF_WEAPON;
}
//======================
//Weapon::Delete
//======================
void Weapon::Delete
(
void
)
{
if( g_iInThinks )
{
DetachGun();
if( owner )
RemoveFromOwner();
PostEvent( EV_Remove, 0 );
}
else
{
delete this;
}
}
//======================
//Weapon::GetScriptOwner
//======================
Listener *Weapon::GetScriptOwner
(
void
)
{
return owner;
}
//======================
//Weapon::GetRank
//======================
int Weapon::GetRank
(
void
)
{
return rank;
}
//======================
//Weapon::GetOrder
//======================
int Weapon::GetOrder
(
void
)
{
return order;
}
//======================
//Weapon::SetRank
//======================
void Weapon::SetRank
(
int order,
int rank
)
{
this->order = order;
this->rank = rank;
}
//======================
//Weapon::SetRankEvent
//======================
void Weapon::SetRankEvent
(
Event *ev
)
{
SetRank( ev->GetInteger( 1 ), ev->GetInteger( 2 ) );
}
//======================
//Weapon::SetAutoPutaway
//======================
void Weapon::SetAutoPutaway
(
Event *ev
)
{
auto_putaway = ev->GetBoolean( 1 );
}
//======================
//Weapon::SetUseNoAmmo
//======================
void Weapon::SetUseNoAmmo
(
Event *ev
)
{
use_no_ammo = ev->GetBoolean( 1 );
}
//======================
//Weapon::SetStartAmmo
//======================
void Weapon::SetStartAmmo
(
Event *ev
)
{
if( g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer )
2016-03-27 11:49:47 +02:00
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
startammo[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::SetDMStartAmmo
//======================
void Weapon::SetDMStartAmmo
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
startammo[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::SetMaxChargeTime
//======================
void Weapon::SetMaxChargeTime
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
max_charge_time[firemodeindex] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetMaxChargeTime
//======================
void Weapon::SetMinChargeTime
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
min_charge_time[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::GetMinChargeTime
//======================
float Weapon::GetMinChargeTime
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
return min_charge_time[ mode ];
}
//======================
//Weapon::GetMinChargeTime
//======================
float Weapon::GetMaxChargeTime
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
return max_charge_time[ mode ];
}
//======================
//Weapon::SetAmmoRequired
//======================
void Weapon::SetAmmoRequired
(
Event *ev
)
{
if( g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer )
2016-03-27 11:49:47 +02:00
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
ammorequired[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::SetDMAmmoRequired
//======================
void Weapon::SetDMAmmoRequired
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
ammorequired[ firemodeindex ] = ev->GetInteger( 1 );
}
//======================
//Weapon::GetStartAmmo
//======================
int Weapon::GetStartAmmo
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
return startammo[ mode ];
}
//======================
//Weapon::GetAmmoType
//======================
str Weapon::GetAmmoType
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
return ammo_type[ mode ];
}
//======================
//Weapon::SetAmmoType
//======================
void Weapon::SetAmmoType
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
if ( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) )
ammo_type[firemodeindex] = ev->GetString( 1 );
else
{
warning( "Weapon::SetAmmoType", "Invalid mode %d\n", firemodeindex );
return;
}
}
//======================
//Weapon::SetAmmoAmount
//======================
void Weapon::SetAmmoAmount
(
int amount,
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
// If the clip can hold ammo, then set the amount in the clip to the specified amount
if( ammo_clip_size[ mode ] )
ammo_in_clip[ mode ] = amount;
}
//======================
//Weapon::GetClipSize
//======================
int Weapon::GetClipSize
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
return ammo_clip_size[ mode ];
else
{
warning( "Weapon::GetClipSize", "Invalid mode %d\n", mode );
return 0;
}
}
//======================
//Weapon::UseAmmo
//======================
void Weapon::UseAmmo
(
int amount,
firemode_t mode
)
{
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if( UnlimitedAmmo( mode ) )
return;
// Remove ammo from the clip if it's available
if( ammo_clip_size[ mode ] )
{
ammo_in_clip[ mode ] -= amount;
if( ammo_in_clip[ mode ] < 0 )
{
warning( "UseAmmo", "Used more ammo than in clip.\n" );
ammo_in_clip[ mode ] = 0;
}
if( !ammo_in_clip[ mode ] )
SetShouldReload( qtrue );
owner->AmmoAmountInClipChanged( ammo_type[ mode ], ammo_in_clip[ mode ] );
}
else
{
assert( owner );
if( owner && owner->isClient() && !UnlimitedAmmo( mode ) )
{
// Remove ammo from the player's inventory
owner->UseAmmo( ammo_type[ mode ], ammorequired[ mode ] );
}
}
}
/*
//======================
//Weapon::GetMuzzlePosition
//======================
void Weapon::GetMuzzlePosition
(
Vector *position,
Vector *forward,
Vector *right,
Vector *up
)
{
orientation_t weap_or, barrel_or, orient;
Vector pos;
vec3_t mat[3]={0,0,0,0,0,0,0,0,0};
vec3_t orient[3];
int i, mi, tagnum;
assert( owner );
// We should always have an owner
if ( !owner )
{
return;
}
// Get the owner's weapon orientation ( this is custom code and doesn't use the GetTag function
// because we need to use the saved off fire_frame and fire_animation indexes from the owner
mi = owner->edict->s.modelindex;
tagnum = gi.Tag_NumForName( mi, current_attachToTag.c_str() );
if ( tagnum < 0 )
{
warning( "Weapon::GetMuzzlePosition", "Could not find tag \"%s\"", current_attachToTag.c_str() );
pos = owner->centroid;
AnglesToAxis( owner->angles, mat );
goto out;
}
// Get the orientation based on the frame and anim stored off in the owner.
// This is to prevent weird timing with getting orientations on different frames of firing
// animations and the orientations will not be consistent.
AnglesToAxis( owner->angles, owner->orientation );
orient = gi.Tag_OrientationEx( mi,
owner->CurrentAnim( legs ),
owner->CurrentFrame( legs ),
tagnum & TAG_MASK,
owner->edict->s.scale,
owner->edict->s.bone_tag,
owner->edict->s.bone_quat,
0,
0,
1.0f,
( owner->edict->s.anim & ANIM_BLEND ) != 0,
( owner->edict->s.torso_anim & ANIM_BLEND ) != 0,
owner->GetFiringAnim(),
owner->GetFiringFrame(),
0,
0,
1.0f
);
// Transform the weapon's orientation through the owner's orientation
// Player orientation is normally based on the player's view, but we need
// it to be based on the model's orientation, so we calculate it here.
AnglesToAxis( owner->angles, orient );
VectorCopy( owner->origin, weap_or.origin );
for ( i=0; i<3; i++ )
{
VectorMA( weap_or.origin, orient.origin[i], orient[i], weap_or.origin );
}
MatrixMultiply( orient.axis, orient, weap_or.axis );
// For debugging
G_DrawCoordSystem( weap_or.origin, weap_or.axis[0], weap_or.axis[1], weap_or.axis[2], 50 );
// Get the tag_barrel orientation from the weapon
if ( !this->GetRawTag( "tag_barrel", &barrel_or ) )
{
//warning( "Weapon::GetMuzzlePosition", "Could not find tag_barrel\n" );
pos = owner->centroid;
AnglesToAxis( owner->angles, mat );
goto out;
}
//gi.DPrintf( "anim:%d frame:%d\n", this->CurrentAnim(), this->CurrentFrame() );
// Translate the barrel's orientation through the weapon's orientation
VectorCopy( weap_or.origin, pos );
for ( i = 0 ; i < 3 ; i++ )
{
VectorMA( pos, barrel_or.origin[i], weap_or.axis[i], pos );
}
MatrixMultiply( barrel_or.axis, weap_or.axis, mat );
#if 0
gi.DPrintf( "own_angles: %0.2f %0.2f %0.2f\n", owner->angles[0], owner->angles[1], owner->angles[2] );
gi.DPrintf( "own_orient: %0.2f %0.2f %0.2f\n", owner->orientation[0][0], owner->orientation[0][1], owner->orientation[0][2] );
gi.DPrintf( "bone forward: %0.2f %0.2f %0.2f\n", orient.axis[0][0], orient.axis[0][1], orient.axis[0][2] );
gi.DPrintf( "barrel forward: %0.2f %0.2f %0.2f\n", barrel_or.axis[0][0], barrel_or.axis[0][1], barrel_or.axis[0][2] );
gi.DPrintf( "weapon forward: %0.2f %0.2f %0.2f\n", weap_or.axis[0][0], weap_or.axis[0][1], weap_or.axis[0][2] );
gi.DPrintf( "mat forward: %0.2f %0.2f %0.2f\n \n", mat[0][0], mat[0][1], mat[0][2] );
#endif
// For debugging
G_DrawCoordSystem( pos, mat[0], mat[1], mat[2], 30 );
// Ok - we now have a position, forward, right, and up
out:
if ( position )
{
*position = pos;
}
if ( forward )
{
*forward = mat[0];
}
if ( right )
{
*right = mat[1];
}
if ( up )
{
*up = mat[2];
}
}
*/
//======================
//Weapon::GetMuzzlePosition
//======================
void Weapon::GetMuzzlePosition
(
Vector *position,
Vector *forward,
Vector *right,
Vector *up,
Vector *vBarrelPos
)
{
orientation_t weap_or, barrel_or, orient;
//orientation_t view_or;
Vector pos, f, r, u, aim_dir;
Vector vAng;
vec3_t mat[ 3 ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int i, tagnum;
Player *owner;
//qboolean bViewShot;
owner = ( Player * )this->owner.Pointer();
assert( owner );
// We should always have an owner
if( !owner )
{
if( IsSubclassOfVehicleTurretGun() )
{
VehicleTurretGun *t = ( VehicleTurretGun * )this;
2023-08-11 03:09:33 +02:00
owner = ( Player * )t->GetRemoteOwner().Pointer();
2016-03-27 11:49:47 +02:00
}
}
// If this is a crosshair or torsoaim weapon, then the dir is specified by the player's torso angles
if( crosshair )
{
Player *player;
// Use the player's torso angles to determine direction, but use the actual barrel to determine position
player = ( Player * )owner;
AngleVectors( player->GetViewAngles(), f, r, u );
if( forward )
*forward = f;
if( right )
*right = r;
if( up )
*up = u;
}
if( current_attachToTag.length() )
{
tagnum = gi.Tag_NumForName( owner->edict->tiki, current_attachToTag.c_str() );
if( tagnum < 0 )
{
warning( "Weapon::GetMuzzlePosition", "Could not find tag \"%s\"", current_attachToTag.c_str() );
pos = owner->origin;
AnglesToAxis( owner->angles, mat );
goto out;
}
// Get the orientation based on the frame and anim stored off in the owner.
// This is to prevent weird timing with getting orientations on different frames of firing
// animations and the orientations will not be consistent.
AnglesToAxis( owner->angles, owner->orientation );
orient = G_TIKI_Orientation( owner->edict, tagnum );
for( i = 0; i < 3; i++ )
{
VectorMA( weap_or.origin, orient.origin[ i ], owner->orientation[ i ], weap_or.origin );
}
MatrixMultiply( orient.axis, owner->orientation, weap_or.axis );
}
else
{
VectorCopy( origin, weap_or.origin );
AnglesToAxis( angles, weap_or.axis );
}
if( !IsSubclassOfInventoryItem() )
{
if( ( weapon_class & WEAPON_CLASS_THROWABLE ) )
{
AngleVectors( owner->GetViewAngles(), mat[ 0 ], mat[ 1 ], mat[ 2 ] );
}
pos = owner->EyePosition();
VectorCopy( pos, weap_or.origin );
}
else
{
// using the weapon's current origin
VectorCopy( origin, weap_or.origin );
AnglesToAxis( angles, weap_or.axis );
}
// For debugging
G_DrawCoordSystem( weap_or.origin, weap_or.axis[ 0 ], weap_or.axis[ 1 ], weap_or.axis[ 2 ], 50 );
pos = weap_or.origin;
// Get the tag_barrel orientation from the weapon
if( !vBarrelPos || !this->GetRawTag( GetTagBarrel(), &barrel_or ) )
2016-03-27 11:49:47 +02:00
{
//warning( "Weapon::GetMuzzlePosition", "Could not find tag_barrel\n" );
if( owner->IsSubclassOfPlayer() )
{
Player *player = ( Player * )owner;
AnglesToAxis( player->GetViewAngles(), weap_or.axis );
}
else
AnglesToAxis( owner->angles, weap_or.axis );
AxisCopy( weap_or.axis, mat );
if( vBarrelPos )
*vBarrelPos = weap_or.origin;
goto out;
}
if( owner->IsSubclassOfPlayer() && !IsSubclassOfTurretGun() )
{
if( vBarrelPos )
*vBarrelPos = origin;
}
else
{
// Translate the barrel's orientation through the weapon's orientation
VectorCopy( weap_or.origin, pos );
for( i = 0; i < 3; i++ )
{
VectorMA( pos, barrel_or.origin[ i ], weap_or.axis[ i ], pos );
}
MatrixMultiply( barrel_or.axis, weap_or.axis, mat );
if( vBarrelPos )
*vBarrelPos = pos;
}
#if 0
gi.DPrintf( "own_angles: %0.2f %0.2f %0.2f\n", owner->angles[0], owner->angles[1], owner->angles[2] );
gi.DPrintf( "own_orient: %0.2f %0.2f %0.2f\n", owner->orientation[0][0], owner->orientation[0][1], owner->orientation[0][2] );
gi.DPrintf( "bone forward: %0.2f %0.2f %0.2f\n", orient.axis[0][0], orient.axis[0][1], orient.axis[0][2] );
gi.DPrintf( "barrel forward: %0.2f %0.2f %0.2f\n", barrel_or.axis[0][0], barrel_or.axis[0][1], barrel_or.axis[0][2] );
gi.DPrintf( "weapon forward: %0.2f %0.2f %0.2f\n", weap_or.axis[0][0], weap_or.axis[0][1], weap_or.axis[0][2] );
gi.DPrintf( "mat forward: %0.2f %0.2f %0.2f\n \n", mat[0][0], mat[0][1], mat[0][2] );
#endif
// Ok - we now have a position, forward, right, and up
out:
// For debugging
G_DrawCoordSystem( pos, mat[ 0 ], mat[ 1 ], mat[ 2 ], 30 );
if( position )
{
*position = pos;
}
if( !forward && !right && !up )
{
return;
}
if( !IsSubclassOfTurretGun() && owner && owner->IsSubclassOfPlayer() )
{
// ...
// fixme?
}
if( pos != vec_zero )
{
// ffs, wondering how they fucking done it in mohaaa...
//aim_dir = barrel_or.origin - pos;
//aim_dir.normalize();
AngleVectors( owner->GetViewAngles(), aim_dir, NULL, NULL );
if( !IsSubclassOfVehicleTurretGun() )
{
goto out2;
}
Vector ang;
vectoangles( mat[ 0 ], ang );
// this is a part of the most annoying function to reverse ever
// fixme
}
// If there is a target, then use the dir based on that
if( aim_target )
{
aim_dir = aim_target->centroid - pos;
aim_dir.normalize();
if( !IsSubclassOfTurretGun() )
{
out2:
AngleVectors( aim_dir.toAngles(), mat[ 0 ], mat[ 1 ], mat[ 2 ] );
if( forward )
*forward = mat[ 0 ];
if( right )
*right = mat[ 1 ];
if( up )
*up = mat[ 2 ];
return;
}
Vector ang;
vectoangles( mat[ 0 ], ang );
// again a very annoying part to reverse
// fixme
}
// If this weapon doesn't have a crosshair specified, then use the mat from the barrel for the directions
if( !crosshair )
{
if( forward )
*forward = mat[ 0 ];
if( right )
*right = mat[ 1 ];
if( up )
*up = mat[ 2 ];
}
}
//======================
//Weapon::SetAmmoClipSize
//======================
void Weapon::SetAmmoClipSize
(
Event * ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
ammo_clip_size[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::SetAmmoInClip
//======================
void Weapon::SetAmmoInClip
(
Event * ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
ammo_in_clip[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::SetShareClip
//======================
void Weapon::SetShareClip
(
Event * ev
)
{
m_bShareClip = qtrue;
}
//======================
//Weapon::SetTagBarrel
//======================
void Weapon::SetTagBarrel(const char* tagBarrel)
{
m_sTagBarrel = tagBarrel;
}
//======================
//Weapon::GetTagBarrel
//======================
str Weapon::GetTagBarrel() const
{
return m_sTagBarrel;
}
2016-03-27 11:49:47 +02:00
//======================
//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->IsSubclassOfSentient() && owner->m_bOvercookDied) {
owner->m_bOvercookDied = false;
return;
2016-03-27 11:49:47 +02:00
}
mc = MuzzleClear();
// If we are in loopfire, we need to keep checking ammo and using it up
if( loopfire[ mode ] )
{
if( HasAmmo( mode ) && mc )
{
// Use up the appropriate amount of ammo, it's already been checked that we have enough
UseAmmo( ammorequired[ mode ], mode );
}
else
{
ForceIdle();
}
}
// If the muzzle is not clear, then change to a clear animation, otherwise punt out.
if( !mc )
{
ForceIdle();
return;
}
GetMuzzlePosition( &pos, &forward, &right, &up, &vBarrel );
ApplyFireKickback(right, 1000.0);
2016-03-27 11:49:47 +02:00
if (firetype[mode] != FT_LANDMINE || CanPlaceLandmine(pos, owner))
2016-03-27 11:49:47 +02:00
{
if (m_fFireSpreadMultAmount[mode] != 0.0f)
2016-03-27 11:49:47 +02:00
{
float fTime = level.time - m_fFireSpreadMultTime[mode];
2016-03-27 11:49:47 +02:00
if (fTime <= m_fFireSpreadMultTimeCap[mode])
2016-03-27 11:49:47 +02:00
{
float fDecay = fTime * m_fFireSpreadMultFalloff[mode];
2016-03-27 11:49:47 +02:00
if (m_fFireSpreadMult[mode] <= 0.0f)
2016-03-27 11:49:47 +02:00
{
m_fFireSpreadMult[mode] -= fDecay;
2016-03-27 11:49:47 +02:00
if (m_fFireSpreadMult[mode] > 0.0f)
{
m_fFireSpreadMult[mode] = 0.0f;
}
2016-03-27 11:49:47 +02:00
}
else
2016-03-27 11:49:47 +02:00
{
m_fFireSpreadMult[mode] -= fDecay;
2016-03-27 11:49:47 +02:00
if (m_fFireSpreadMult[mode] < 0.0f)
2016-03-27 11:49:47 +02:00
{
m_fFireSpreadMult[mode] = 0.0f;
2016-03-27 11:49:47 +02:00
}
}
}
else
{
m_fFireSpreadMult[mode] = 0.0f;
2016-03-27 11:49:47 +02:00
}
m_fFireSpreadMultTime[mode] = level.time;
2016-03-27 11:49:47 +02:00
}
switch (firetype[mode])
2016-03-27 11:49:47 +02:00
{
case FT_PROJECTILE:
ProjectileAttack(pos,
forward,
owner,
projectileModel[mode],
charge_fraction
);
break;
case FT_LANDMINE:
PlaceLandmine(pos, owner, projectileModel[mode], this);
break;
case FT_DEFUSE:
DefuseObject(right, owner, bulletrange[mode]);
break;
case FT_BULLET:
2016-03-27 11:49:47 +02:00
{
Vector vSpread;
float fSpreadFactor;
int tracerFrequency;
SafePtr<Sentient> ownerPtr;
if (owner)
{
if (owner->client)
{
Player* player = (Player*)owner.Pointer();
fSpreadFactor = player->velocity.length() / sv_runspeed->integer;
if (fSpreadFactor > 1.0f)
{
fSpreadFactor = 1.0f;
}
vSpread = bulletspreadmax[mode] * fSpreadFactor;
fSpreadFactor = 1.0f - fSpreadFactor;
vSpread += bulletspread[mode] * fSpreadFactor;
vSpread *= m_fFireSpreadMult[mode] + 1.0f;
if (m_iZoom)
{
if (player->IsSubclassOfPlayer() && player->IsZoomed())
{
vSpread *= 1.0f + fSpreadFactor * (m_fZoomSpreadMult - 1.0f);
}
}
}
}
else
{
vSpread = (bulletspreadmax[mode] + bulletspread[mode]) * 0.5f;
}
if (!g_gametype->integer && 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]
);
2016-03-27 11:49:47 +02:00
}
break;
case FT_FAKEBULLET:
2016-03-27 11:49:47 +02:00
{
Vector vSpread;
float fSpreadFactor;
2016-03-27 11:49:47 +02:00
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);
2016-03-27 11:49:47 +02:00
}
else
{
VehicleTurretGun* turret = (VehicleTurretGun*)this;
2016-03-27 11:49:47 +02:00
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:
2016-03-27 11:49:47 +02:00
{
Vector melee_pos, melee_end;
Vector dir;
float damage;
meansOfDeath_t meansofdeath;
float knockback;
2016-03-27 11:49:47 +02:00
if (owner)
{
dir = owner->centroid - pos;
}
else
{
dir = centroid - pos;
}
2016-03-27 11:49:47 +02:00
dir.z = 0;
2016-03-27 11:49:47 +02:00
melee_pos = pos - forward * dir.length();
melee_end = melee_pos + forward * bulletrange[mode];
2016-03-27 11:49:47 +02:00
damage = bulletdamage[mode];
knockback = 0;
2016-03-27 11:49:47 +02:00
meansofdeath = GetMeansOfDeath(mode);
2016-03-27 11:49:47 +02:00
Container<Entity*>victimlist;
2016-03-27 11:49:47 +02:00
m_iNumShotsFired++;
if (MeleeAttack(melee_pos, melee_end, damage, owner, meansofdeath, 8, -8, 8, knockback, true, &victimlist))
{
m_iNumHits++;
m_iNumTorsoShots++;
}
}
break;
2016-03-27 11:49:47 +02:00
}
if (!quiet[firemodeindex])
2016-03-27 11:49:47 +02:00
{
if (next_noise_time <= level.time)
{
BroadcastAIEvent(AI_EVENT_WEAPON_FIRE);
next_noise_time = level.time + 1;
}
2016-03-27 11:49:47 +02:00
}
if (owner && owner->client)
2016-03-27 11:49:47 +02:00
{
Vector vAngles = owner->GetViewAngles();
2016-03-27 11:49:47 +02:00
if (viewkickmin[mode][0] != 0.0f || viewkickmax[mode][0] != 0.0f)
{
vAngles[0] += random() * (viewkickmax[mode][0] - viewkickmin[mode][0]) + viewkickmin[mode][0];
}
2016-03-27 11:49:47 +02:00
if (viewkickmin[1][0] != 0.0f || viewkickmax[1][0] != 0.0f)
{
vAngles[1] += random() * (viewkickmax[mode][1] - viewkickmin[mode][1]) + viewkickmin[mode][1];
}
2016-03-27 11:49:47 +02:00
owner->SetViewAngles(vAngles);
}
if (m_fFireSpreadMultAmount[mode])
2016-03-27 11:49:47 +02:00
{
m_fFireSpreadMult[mode] += m_fFireSpreadMultAmount[mode];
if (m_fFireSpreadMultCap[mode] < 0.0f)
2016-03-27 11:49:47 +02:00
{
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;
}
2016-03-27 11:49:47 +02:00
}
else if (m_fFireSpreadMult[mode] <= m_fFireSpreadMultCap[mode] &&
m_fFireSpreadMult[mode] < 0.0f)
2016-03-27 11:49:47 +02:00
{
m_fFireSpreadMult[mode] = 0.0f;
2016-03-27 11:49:47 +02:00
}
}
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]);
2016-03-27 11:49:47 +02:00
}
}
}
//======================
//Weapon::ApplyFireKickback
//======================
void Weapon::ApplyFireKickback(const Vector& org, float kickback)
{
}
2016-03-27 11:49:47 +02:00
//======================
//Weapon::SetAimAnim
//======================
void Weapon::SetAimAnim
(
Event *ev
)
{
str anim;
anim = ev->GetString( 1 );
aimanim = gi.Anim_NumForName( edict->tiki, anim.c_str() );
aimframe = ev->GetInteger( 2 );
}
//======================
//Weapon::SetOwner
//======================
void Weapon::SetOwner
(
Sentient *ent
)
{
assert( ent );
if ( !ent )
{
// return to avoid any buggy behaviour
return;
}
Item::SetOwner( ent );
setOrigin( vec_zero );
setAngles( vec_zero );
}
//======================
//Weapon::AttachToHand
//======================
void Weapon::AttachToHand
(
Event *ev
)
{
str tag;
if( !owner || !attached ) {
return;
}
weaponhand_t hand = WeaponHandNameToNum( ev->GetString( 1 ) );
if( hand == WEAPON_ERROR ) {
return;
}
if( hand == WEAPON_OFFHAND )
{
tag = attachToTag_offhand;
}
else
{
tag = attachToTag_main;
}
current_attachToTag = tag;
int tagnum = gi.Tag_NumForName( owner->edict->tiki, tag );
if( tagnum < 0 )
{
warning( "Weapon::AttachToHand", "Attachment of weapon '%s' to tag \"%s\": Tag Not Found\n", getName().c_str(), tag.c_str() );
}
else
{
attach( owner->entnum, tagnum );
setOrigin();
}
}
//======================
//Weapon::SetCantPartialReload
//======================
void Weapon::SetCantPartialReload
(
Event *ev
)
{
if( g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer )
2016-03-27 11:49:47 +02:00
return;
m_bCanPartialReload = qfalse;
}
//======================
//Weapon::SetDMCantPartialReload
//======================
void Weapon::SetDMCantPartialReload
(
Event *ev
)
{
if( !g_gametype->integer )
return;
m_bCanPartialReload = qfalse;
}
//======================
//Weapon::AddAdditionalStartAmmo
//======================
void Weapon::AddAdditionalStartAmmo(Event* ev)
{
2023-07-31 23:41:53 +02:00
m_additionalStartAmmoTypes.AddObject(ev->GetString(1));
m_additionalStartAmmoAmounts.AddObject(ev->GetInteger(2));
}
//======================
//Weapon::AddStartItem
//======================
void Weapon::AddStartItem(Event* ev)
{
2023-07-31 23:41:53 +02:00
m_startItems.AddObject(ev->GetString(1));
}
2016-03-27 11:49:47 +02:00
//======================
//Weapon::AmmoAvailable
//======================
int Weapon::AmmoAvailable
(
firemode_t mode
)
{
// Returns the amount of ammo the owner has that is available for use
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
// Make sure there is an owner before querying the amount of ammo
if( owner )
{
return owner->AmmoCount( ammo_type[ mode ] );
}
else
{
if( ammo_clip_size[ mode ] )
{
return ammo_clip_size[ mode ];
}
else
{
return ammorequired[ mode ];
}
}
}
//======================
//Weapon::UnlimitedAmmo
//======================
qboolean Weapon::UnlimitedAmmo
(
firemode_t mode
)
{
if( !owner )
{
return false;
}
if( !owner->isClient() || DM_FLAG( DF_INFINITE_AMMO ) )
{
return true;
}
else if( !Q_stricmp( ammo_type[ mode ], "None" ) )
{
return true;
}
return false;
}
//======================
//Weapon::HasAmmo
//======================
qboolean Weapon::HasAmmo
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
if( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
{
warning( "Weapon::HasAmmo", "Invalid mode %d\n", mode );
return false;
}
if( UnlimitedAmmo( mode ) )
{
return true;
}
// If the weapon uses a clip, check for ammo in the right clip
if( ammo_clip_size[ mode ] && HasAmmoInClip( mode ) )
{
return true;
}
else // Otherwise check if ammo is available in general
{
if( !ammorequired[ mode ] )
return true;
return ( AmmoAvailable( mode ) >= ammorequired[ mode ] );
}
}
//======================
//Weapon::HasAmmoInClip
//======================
qboolean Weapon::HasAmmoInClip
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
if( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
warning( "Weapon::HasAmmoInClip", "Invalid mode %d\n", mode );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if( ammo_clip_size[ mode ] )
{
if( ammo_in_clip[ mode ] >= ammorequired[ mode ] )
return true;
}
else
{
if( !ammorequired[ mode ] )
return true;
return ( AmmoAvailable( mode ) >= ammorequired[ mode ] );
}
return false;
}
//======================
//Weapon::IsSemiAuto
//======================
qboolean Weapon::IsSemiAuto
(
void
)
{
return m_bSemiAuto;
}
//======================
//Weapon::GetState
//======================
weaponstate_t Weapon::GetState
(
void
)
{
return weaponstate;
}
//======================
//Weapon::ForceState
//======================
void Weapon::ForceState
(
weaponstate_t state
)
{
weaponstate = state;
}
//======================
//Weapon::MuzzleClear
//======================
qboolean Weapon::MuzzleClear
(
void
)
{
return qtrue;
}
//======================
//Weapon::ReadyToFire
//======================
qboolean Weapon::ReadyToFire
(
firemode_t mode,
qboolean playsound
)
{
if (owner && owner->IsSubclassOfSentient()) {
// Clear the cook flag
owner->m_bOvercookDied = false;
}
if( !level.playerfrozen && m_iZoom && mode == FIRE_SECONDARY ) {
2016-03-27 11:49:47 +02:00
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 ) ) )
2016-03-27 11:49:47 +02:00
{
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);
2023-08-03 21:43:50 +02:00
next_maxmovement_time = level.time + level.frametime + G_Random(0.1f) + 0.95f;
}
2016-03-27 11:49:47 +02:00
}
if (playsound && (level.time > next_noammo_time))
{
Sound(m_NoAmmoSound);
next_noammo_time = level.time + level.frametime + G_Random(0.1f) + 0.95f;
}
2016-03-27 11:49:47 +02:00
}
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::IsCarryableTurret
//======================
bool Weapon::IsCarryableTurret()
{
return false;
}
2016-03-27 11:49:47 +02:00
//======================
//Weapon::Drop
//======================
qboolean Weapon::Drop
(
void
)
{
Vector temp;
if( !owner )
{
return false;
}
if( !IsDroppable() )
{
return false;
}
if( attached )
{
Vector vAng;
orientation_t oTag;
Entity *pParent;
if( edict->s.parent == ENTITYNUM_NONE || edict->s.tag_num < 0 )
{
pParent = owner;
vAng = pParent->angles;
}
else
{
vAng = vec_zero;
pParent = G_GetEntity( edict->s.parent );
AnglesToAxis( pParent->angles, pParent->orientation );
pParent->GetTag( edict->s.tag_num, &oTag );
MatrixToEulerAngles( oTag.axis, vAng );
}
setAngles( vAng );
DetachGun();
}
else
{
temp[ 2 ] = 40;
setOrigin( owner->origin + temp );
setAngles( owner->angles );
}
setSize( Vector( -12, -12, -2 ), Vector( 12, 12, 12 ) );
// stop animating
StopWeaponAnim();
// drop the weapon
PlaceItem();
temp = centroid - owner->centroid;
temp[ 2 ] = 0;
VectorNormalize( temp );
temp *= 75.0f;
temp[ 0 ] += G_CRandom( 25 );
temp[ 1 ] += G_CRandom( 25 );
temp[ 2 ] = G_CRandom( 50 ) + 150;
velocity = owner->velocity * 0.5 + temp;
avelocity = Vector( 0, G_CRandom( 120 ), 0 );
spawnflags |= DROPPED_PLAYER_ITEM;
if( owner && owner->isClient() )
{
if( owner->deadflag && g_dropclips->integer > 0 )
{
int ammo;
if( ammo_clip_size[ FIRE_PRIMARY ] )
startammo[ FIRE_PRIMARY ] = g_dropclips->integer * ammo_in_clip[ FIRE_PRIMARY ];
else
startammo[ FIRE_PRIMARY ] = g_dropclips->integer;
ammo = AmmoAvailable( FIRE_PRIMARY );
if( startammo[ FIRE_PRIMARY ] > ammo )
{
startammo[ FIRE_PRIMARY ] = ammo;
}
if( ammo_clip_size[ FIRE_SECONDARY ] )
startammo[ FIRE_SECONDARY ] = g_dropclips->integer * ammo_in_clip[ FIRE_SECONDARY ];
else
startammo[ FIRE_SECONDARY ] = g_dropclips->integer;
ammo = AmmoAvailable( FIRE_SECONDARY );
if( startammo[ FIRE_SECONDARY ] > ammo )
{
startammo[ FIRE_SECONDARY ] = ammo;
}
ammo_in_clip[ FIRE_PRIMARY ] = 0;
ammo_in_clip[ FIRE_SECONDARY ] = 0;
}
else
{
startammo[ FIRE_PRIMARY ] = AmmoAvailable( FIRE_PRIMARY );
owner->takeAmmoType( ammo_type[ FIRE_PRIMARY ] );
}
}
else
{
if( ammo_clip_size[ FIRE_PRIMARY ] && ammo_in_clip[ FIRE_PRIMARY ] )
startammo[ FIRE_PRIMARY ] = ammo_in_clip[ FIRE_PRIMARY ];
else
startammo[ FIRE_PRIMARY ] >>= 2;
if( ammo_clip_size[ FIRE_SECONDARY ] && ammo_in_clip[ FIRE_SECONDARY ] )
startammo[ FIRE_SECONDARY ] = ammo_in_clip[ FIRE_SECONDARY ];
else
startammo[ FIRE_SECONDARY ] >>= 2;
if( startammo[ FIRE_PRIMARY ] == 0 )
{
startammo[ FIRE_PRIMARY ] = 1;
}
if( startammo[ FIRE_SECONDARY ] == 0 )
{
startammo[ FIRE_SECONDARY ] = 1;
}
}
// Wait some time before the last owner can pickup this weapon
last_owner = owner;
last_owner_trigger_time = level.time + 2.0f;
// Cancel reloading events
CancelEventsOfType( EV_Weapon_DoneReloading );
// Remove this from the owner's item list
RemoveFromOwner();
PostEvent( EV_Remove, g_droppeditemlife->value );
PostEvent( EV_Weapon_FallingAngleAdjust, level.frametime );
return true;
}
//======================
//Weapon::Charge
//======================
void Weapon::Charge
(
firemode_t mode
)
{
}
//======================
//Weapon::OnOverCookedWarning
//======================
void Weapon::OnOverCookedWarning(Event* ev)
{
}
//======================
//Weapon::OnOverCooked
//======================
void Weapon::OnOverCooked(Event* ev)
{
2023-07-31 23:41:53 +02:00
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);
}
}
}
2016-03-27 11:49:47 +02:00
//======================
//Weapon::ReleaseFire
//======================
void Weapon::ReleaseFire
(
firemode_t mode,
float charge_time
)
{
// Calculate and store off the charge fraction to use when the weapon actually shoots
// Clamp to max_charge_time
if( charge_time - min_charge_time[ mode ] >= 0.0f )
{
if( charge_time <= max_charge_time[ mode ] )
charge_fraction = charge_time / max_charge_time[ mode ];
else
charge_fraction = 1.0f;
}
else
{
charge_fraction = 0.0f;
}
// Call regular fire function
Fire( mode );
}
//======================
//Weapon::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";
}
}
2016-03-27 11:49:47 +02:00
//======================
//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;
}
}
2016-03-27 11:49:47 +02:00
if( ammo_clip_size[ FIRE_PRIMARY ] && !ammo_in_clip[ FIRE_PRIMARY ] && HasAnim( "fire_empty" ) )
{
SetWeaponAnim( "fire_empty", done_event );
}
else
{
SetWeaponAnim( GetFireAnim(), done_event);
2016-03-27 11:49:47 +02:00
}
}
else if( mode == FIRE_SECONDARY )
{
if( ( ( !m_bShareClip && ammo_clip_size[ FIRE_SECONDARY ] && !ammo_in_clip[ FIRE_SECONDARY ] ) ||
( m_bShareClip && ammo_clip_size[ FIRE_PRIMARY ] && !ammo_in_clip[ FIRE_PRIMARY ] ) ) && HasAnim( "fire_empty" ) )
{
SetWeaponAnim( "secondaryfire_empty", done_event );
}
else
{
SetWeaponAnim( "secondaryfire", done_event );
}
}
}
//======================
//Weapon::DetachGun
//======================
void Weapon::DetachGun
(
void
)
{
if( attached )
{
if( m_iZoom && owner && owner->IsSubclassOfPlayer() )
{
Player *p = ( Player * )owner.Pointer();
p->ZoomOff();
}
StopSound( CHAN_WEAPONIDLE );
attached = false;
detach();
hideModel();
}
}
//======================
//Weapon::AttachGun
//======================
void Weapon::AttachGun
(
weaponhand_t hand,
qboolean holstering
)
{
int tag_num;
if( !owner )
{
current_attachToTag = "";
return;
}
if( attached )
{
DetachGun();
}
if( holstering )
{
// Save off these values if we are holstering the weapon. We will restore them when
// the users raises the weapons again.
lastAngles = this->angles;
lastScale = this->edict->s.scale;
lastValid = qtrue;
}
else if( lastValid )
{
// Restore the last
setScale( lastScale );
setAngles( lastAngles );
lastValid = qfalse;
}
switch( hand )
{
case WEAPON_MAIN:
if( holstering )
{
current_attachToTag = holster_attachToTag;
setAngles( holsterAngles );
setScale( holsterScale );
}
else
{
current_attachToTag = attachToTag_main;
}
break;
case WEAPON_OFFHAND:
if( holstering )
{
current_attachToTag = holster_attachToTag;
setAngles( holsterAngles );
setScale( holsterScale );
}
else
{
current_attachToTag = attachToTag_offhand;
}
break;
default:
warning( "Weapon::AttachGun", "Invalid hand for attachment of weapon specified" );
break;
}
if( !current_attachToTag.length() )
return;
if( owner->edict->tiki )
{
tag_num = gi.Tag_NumForName( owner->edict->tiki, this->current_attachToTag.c_str() );
NoLerpThisFrame();
if( tag_num >= 0 )
{
attached = true;
attach( owner->entnum, tag_num );
showModel();
setOrigin();
}
else
{
warning( "Weapon::AttachGun", "Attachment of weapon to tag \"%s\": Tag Not Found\n", this->current_attachToTag.c_str() );
}
}
if( m_bAutoZoom && owner->IsSubclassOfPlayer() )
{
Player *p = ( Player * )owner.Pointer();
p->ToggleZoom( m_iZoom );
}
}
//======================
//Weapon::GiveStartingAmmoToOwner
//======================
void Weapon::GiveStartingAmmoToOwner
(
Event *ev
)
{
str ammotype;
int mode;
int i;
2016-03-27 11:49:47 +02:00
assert( owner );
if( !owner )
{
warning( "Weapon::GiveStartingAmmoToOwner", "Owner not found\n" );
return;
}
// Give the player the starting ammo
for( mode = FIRE_PRIMARY; mode<MAX_FIREMODES; mode++ )
{
ammotype = GetAmmoType( ( firemode_t )( mode ) );
if( ammotype.length() )
{
int start_ammo = this->GetStartAmmo( ( firemode_t )mode );
if( ammo_clip_size[ mode ] )
{
int ammo = ammo_clip_size[ mode ] - ammo_in_clip[ mode ];
if( ammo > 0 )
{
if( ammo < start_ammo )
{
start_ammo -= ammo;
ammo_in_clip[ mode ] = ammo + ammo_in_clip[ mode ];
}
else
{
ammo_in_clip[ mode ] = start_ammo + ammo_in_clip[ mode ];
2016-08-02 16:54:02 +02:00
start_ammo = 0;
2016-03-27 11:49:47 +02:00
}
}
}
2016-08-02 16:54:02 +02:00
if( start_ammo )
{
owner->GiveAmmo( ammotype, start_ammo );
}
2016-03-27 11:49:47 +02:00
}
}
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();
}
2016-03-27 11:49:47 +02:00
}
//======================
//Weapon::PickupWeapon
//======================
void Weapon::PickupWeapon
(
Event *ev
)
{
Sentient *sen;
Entity *other;
qboolean hasweapon;
qboolean hasclass;
int iGiveAmmo;
str realname;
2016-03-27 11:49:47 +02:00
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);
2016-03-27 11:49:47 +02:00
if( (g_gametype->integer || g_realismmode->integer)
&& !hasclass
&& !IsSecondaryWeapon()
&& sen->HasPrimaryWeapon()
)
2016-03-27 11:49:47 +02:00
{
// 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;
}
2016-03-27 11:49:47 +02:00
if( !hasweapon && !hasclass )
2016-03-27 11:49:47 +02:00
{
if( other->IsSubclassOfPlayer() )
{
gi.SendServerCommand( other->edict - g_entities, "print \"" HUD_MESSAGE_YELLOW "%s\n\"", gi.LV_ConvertString( va( "Picked Up %s", item_name.c_str() ) ) );
if( !( spawnflags & DROPPED_PLAYER_ITEM ) && !( spawnflags & DROPPED_ITEM ) )
{
ItemPickup( other );
return;
}
if( Pickupable( other ) )
{
setMoveType( MOVETYPE_NONE );
setSolidType( SOLID_NOT );
hideModel();
velocity = vec_zero;
avelocity = vec_zero;
CancelEventsOfType( EV_Remove );
CancelEventsOfType( EV_Weapon_FallingAngleAdjust );
DetachFromOwner();
current_attachToTag = "";
lastValid = qfalse;
edict->s.tag_num = -1;
edict->s.attach_use_angles = qfalse;
VectorClear(edict->s.attach_offset);
2016-03-27 11:49:47 +02:00
setOrigin( vec_zero );
setAngles( vec_zero );
SetOwner( sen );
2016-03-27 11:49:47 +02:00
sen->AddItem( this );
sen->ReceivedItem( this );
Sound( sPickupSound );
}
}
}
else
{
str sAmmoName = ammo_type[ FIRE_PRIMARY ];
if( sen->AmmoCount( sAmmoName ) != sen->MaxAmmoCount( sAmmoName ) )
{
setSolidType( SOLID_NOT );
hideModel();
CancelEventsOfType( EV_Item_DropToFloor );
CancelEventsOfType( EV_Item_Respawn );
CancelEventsOfType( EV_FadeOut );
CancelEventsOfType( EV_Remove );
CancelEventsOfType( EV_Weapon_FallingAngleAdjust );
if( Respawnable() )
{
PostEvent( EV_Item_Respawn, 0 );
}
else
{
PostEvent( EV_Remove, 0 );
}
Sound( m_sAmmoPickupSound );
}
}
if (startammo[FIRE_PRIMARY] && ammo_type[FIRE_PRIMARY].length() && other->isClient())
{
str sMessage;
const str& sAmmoType = ammo_type[FIRE_PRIMARY];
iGiveAmmo = startammo[FIRE_PRIMARY];
sen->GiveAmmo(sAmmoType, iGiveAmmo);
if (!g_gametype->integer && other->IsSubclassOfPlayer()) {
if (!sAmmoType.icmp("agrenade"))
{
if (iGiveAmmo == 1)
sMessage = gi.LV_ConvertString("Got 1 Grenade");
else
sMessage = gi.LV_ConvertString(va("Got %i Grenades", iGiveAmmo));
}
}
if (!sAmmoType.icmp("grenade"))
{
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]) {
if (startammo[FIRE_SECONDARY] && ammo_type[FIRE_SECONDARY].length() && other->isClient()) {
str sMessage;
const str& sAmmoType = ammo_type[FIRE_PRIMARY];
iGiveAmmo = startammo[FIRE_SECONDARY];
sen->GiveAmmo(sAmmoType, iGiveAmmo);
if (!g_gametype->integer && other->IsSubclassOfPlayer()) {
if (!sAmmoType.icmp("agrenade"))
{
if (iGiveAmmo == 1)
sMessage = gi.LV_ConvertString("Got 1 Grenade");
else
sMessage = gi.LV_ConvertString(va("Got %i Grenades", iGiveAmmo));
}
}
if (!sAmmoType.icmp("grenade"))
{
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));
}
2016-03-27 11:49:47 +02:00
else
{
sMessage = gi.LV_ConvertString(va("Got %i %s Rounds", startammo[FIRE_PRIMARY], sAmmoType.c_str()));
}
2016-03-27 11:49:47 +02:00
gi.SendServerCommand(other->edict - g_entities, "print \"" HUD_MESSAGE_YELLOW "%s\n\"", sMessage.c_str());
}
2016-03-27 11:49:47 +02:00
}
Unregister( STRING_PICKUP );
}
//======================
//Weapon::IsSecondaryWeapon
//======================
qboolean Weapon::IsSecondaryWeapon
(
void
)
{
return ( weapon_class & WEAPON_CLASS_SECONDARY );
}
//======================
//Weapon::ForceIdle
//======================
void Weapon::ForceIdle
(
void
)
{
SetWeaponIdleAnim();
// Force the weapon to the idle animation
weaponstate = WEAPON_READY;
}
//======================
//Weapon::SetWeaponAnim
//======================
qboolean Weapon::SetWeaponAnim
(
const char *anim,
Event *ev
)
{
int animnum = gi.Anim_NumForName( edict->tiki, anim );
if( animnum == -1 )
{
if( ev ) {
delete ev;
}
return qfalse;
}
StopAnimating( m_iAnimSlot );
RestartAnimSlot( m_iAnimSlot );
2016-03-27 11:49:47 +02:00
int idleanim = gi.Anim_NumForName( edict->tiki, "idle" );
edict->s.frameInfo[ m_iAnimSlot ].index = idleanim;
m_iAnimSlot = ( m_iAnimSlot + 1 ) & 3;
edict->s.frameInfo[ m_iAnimSlot ].index = idleanim;
if( ev )
{
NewAnim( animnum, ev, m_iAnimSlot );
}
else
{
NewAnim( animnum, m_iAnimSlot );
}
SetOnceType( m_iAnimSlot );
RestartAnimSlot( m_iAnimSlot );
2016-03-27 11:49:47 +02:00
return qtrue;
}
//======================
//Weapon::SetWeaponAnim
//======================
qboolean Weapon::SetWeaponAnim
(
const char *anim,
Event& ev
)
{
Event *event = new Event( ev );
return SetWeaponAnim( anim, event );
}
//======================
//Weapon::SetWeaponAnimEvent
//======================
void Weapon::SetWeaponAnimEvent
(
Event *ev
)
{
SetWeaponAnim( ev->GetString( 1 ) );
}
//======================
//Weapon::SetWeaponIdleAnim
//======================
void Weapon::SetWeaponIdleAnim
(
void
)
{
if (m_bShareClip) {
if (ammo_clip_size[FIRE_PRIMARY] && !ammo_in_clip[FIRE_PRIMARY])
2016-03-27 11:49:47 +02:00
{
if (SetWeaponAnim("idle_empty"))
{
return;
}
}
SetWeaponAnim("idle");
} else {
if (ammo_clip_size[FIRE_PRIMARY] && !ammo_in_clip[FIRE_PRIMARY])
{
if (SetWeaponAnim("idle_empty"))
{
return;
}
2016-03-27 11:49:47 +02:00
}
}
SetWeaponAnim("idle");
2016-03-27 11:49:47 +02:00
}
//======================
//Weapon::SetWeaponIdleAnimEvent
//======================
void Weapon::SetWeaponIdleAnimEvent
(
Event *ev
)
{
SetWeaponIdleAnim();
}
//======================
//Weapon::StopWeaponAnim
//======================
void Weapon::StopWeaponAnim
(
void
)
{
int animnum;
2016-03-27 11:49:47 +02:00
RestartAnimSlot( m_iAnimSlot );
StopAnimating( m_iAnimSlot );
2016-03-27 11:49:47 +02:00
animnum = gi.Anim_NumForName( edict->tiki, "idle" );
StartAnimSlot(m_iAnimSlot, animnum, 1.f);
2016-03-27 11:49:47 +02:00
m_iAnimSlot = ( m_iAnimSlot + 1 ) & 3;
}
//======================
//Weapon::DoneRaising
//======================
void Weapon::DoneRaising
(
Event *ev
)
{
weaponstate = WEAPON_READY;
ForceIdle();
if( !owner )
{
PostEvent( EV_Remove, 0 );
return;
}
}
//======================
//Weapon::ClientFireDone
//======================
void Weapon::ClientFireDone
(
void
)
{
// This is called when the client's firing animation is done
}
//======================
//Weapon::DoneFiring
//======================
void Weapon::DoneFiring
(
Event *ev
)
{
// This is called when the weapon's firing animation is done
ForceIdle();
// Check to see if the auto_putaway flag is set, and the weapon is out of ammo. If so, then putaway the
// weapon.
if( !HasAmmo( FIRE_PRIMARY ) && auto_putaway )
{
PutAway();
}
}
//======================
//Weapon::DoneReloading
//======================
void Weapon::DoneReloading
(
Event *ev
)
{
SetShouldReload( qfalse );
weaponstate = WEAPON_READY;
}
//======================
//Weapon::CheckReload
//======================
qboolean Weapon::CheckReload
(
firemode_t mode
)
{
// Check to see if the weapon needs to be reloaded
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if( putaway )
return false;
if( ammo_in_clip[ mode ] < ammo_clip_size[ mode ] && AmmoAvailable( mode ) && ( m_bCanPartialReload || ammo_in_clip[ mode ] <= 0 ) )
return true;
return false;
}
//======================
//Weapon::Idle
//======================
void Weapon::Idle
(
Event *ev
)
{
ForceIdle();
}
//======================
//Weapon::IdleInit
//======================
void Weapon::IdleInit
(
Event *ev
)
{
for( int i = 7; i >= 0; i-- )
{
SetWeaponAnim( "idle" );
}
weaponstate = WEAPON_READY;
}
//======================
//Weapon::GetMaxRange
//======================
float Weapon::GetMaxRange
(
void
)
{
return maxrange;
}
//======================
//Weapon::GetMinRange
//======================
float Weapon::GetMinRange
(
void
)
{
return minrange;
}
//======================
//Weapon::SetMaxRangeEvent
//======================
void Weapon::SetMaxRangeEvent
(
Event *ev
)
{
maxrange = ev->GetFloat( 1 );
}
//======================
//Weapon::SetMinRangeEvent
//======================
void Weapon::SetMinRangeEvent
(
Event *ev
)
{
minrange = ev->GetFloat( 1 );
}
//======================
//Weapon::NotDroppableEvent
//======================
void Weapon::NotDroppableEvent
(
Event *ev
)
{
notdroppable = true;
}
//======================
//Weapon::SetMaxRange
//======================
void Weapon::SetMaxRange
(
float val
)
{
maxrange = val;
}
//======================
//Weapon::SetMinRange
//======================
void Weapon::SetMinRange
(
float val
)
{
minrange = val;
}
//======================
//Weapon::WeaponSound
//======================
void Weapon::WeaponSound
(
Event *ev
)
{
Event *e;
// Broadcasting a sound can be time consuming. Only do it once in a while on really fast guns.
if( nextweaponsoundtime > level.time )
{
if( owner )
{
owner->BroadcastAIEvent( AI_EVENT_WEAPON_FIRE );
}
else
{
BroadcastAIEvent( AI_EVENT_WEAPON_FIRE );
}
return;
}
if( owner )
{
e = new Event( ev );
owner->ProcessEvent( e );
}
else
{
Item::BroadcastAIEvent( AI_EVENT_WEAPON_FIRE );
}
// give us some breathing room
nextweaponsoundtime = level.time + 0.4;
}
//======================
//Weapon::Removable
//======================
qboolean Weapon::Removable
(
void
)
{
if (
( ( int )( dmflags->integer ) & DF_WEAPONS_STAY ) &&
!( spawnflags & ( DROPPED_ITEM | DROPPED_PLAYER_ITEM ) )
)
return false;
else
return true;
}
//======================
//Weapon::Pickupable
//======================
qboolean Weapon::Pickupable
(
Entity* other
)
2016-03-27 11:49:47 +02:00
{
Sentient* sen;
2016-03-27 11:49:47 +02:00
if (!other->IsSubclassOfSentient())
{
return false;
}
else if (!other->isClient())
{
return false;
}
2016-03-27 11:49:47 +02:00
sen = (Sentient*)other;
2016-03-27 11:49:47 +02:00
//FIXME
// This should be in player
2016-03-27 11:49:47 +02:00
// 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;
2016-03-27 11:49:47 +02:00
weapon = (Weapon*)sen->FindItem(getName());
2016-03-27 11:49:47 +02:00
if (weapon)
return false;
}
2016-03-27 11:49:47 +02:00
return true;
}
2016-03-27 11:49:47 +02:00
//======================
//Weapon::AutoChange
//======================
qboolean Weapon::AutoChange
(
void
)
{
return true;
}
//======================
//Weapon::ClipAmmo
//======================
int Weapon::ClipAmmo
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if( ammo_clip_size[ mode ] )
return ammo_in_clip[ mode ];
else
return -1;
}
//======================
//Weapon::ProcessWeaponCommandsEvent
//======================
void Weapon::ProcessWeaponCommandsEvent
(
Event *ev
)
{
int index;
index = ev->GetInteger( 1 );
ProcessInitCommands();
}
//======================
//Weapon::SetActionLevelIncrement
//======================
void Weapon::SetActionLevelIncrement
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
action_level_increment[firemodeindex] = ev->GetInteger( 1 );
}
//======================
//Weapon::ActionLevelIncrement
//======================
int Weapon::ActionLevelIncrement
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
if ( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
return action_level_increment[mode];
else
{
warning( "Weapon::ActionLevelIncrement", "Invalid mode %d\n", mode );
return 0;
}
}
//======================
//Weapon::IsDroppable
//======================
qboolean Weapon::IsDroppable
(
void
)
{
if( notdroppable )
{
return false;
}
else
{
return true;
}
}
//======================
//Weapon::SetFireType
//======================
void Weapon::SetFireType(Event* ev)
{
str ftype;
ftype = ev->GetString(1);
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
if (!ftype.icmp("projectile"))
firetype[firemodeindex] = FT_PROJECTILE;
else if (!ftype.icmp("bullet"))
firetype[firemodeindex] = FT_BULLET;
else if (!ftype.icmp("fakebullet"))
firetype[firemodeindex] = FT_FAKEBULLET;
else if (!ftype.icmp("melee"))
firetype[firemodeindex] = FT_MELEE;
else if (!ftype.icmp("special_projectile"))
firetype[firemodeindex] = FT_SPECIAL_PROJECTILE;
else if (!ftype.icmp("clickitem"))
firetype[firemodeindex] = FT_CLICKITEM;
else if (!ftype.icmp("heavy"))
firetype[firemodeindex] = FT_HEAVY;
else if (!ftype.icmp("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());
2016-03-27 11:49:47 +02:00
}
//======================
//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 )
2016-03-27 11:49:47 +02:00
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
projectileModel[ firemodeindex ] = ev->GetString( 1 );
CacheResource( projectileModel[ firemodeindex ].c_str() );
}
//======================
//Weapon::SetDMProjectile
//======================
void Weapon::SetDMProjectile
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
projectileModel[ firemodeindex ] = ev->GetString( 1 );
CacheResource( projectileModel[ firemodeindex ].c_str() );
}
//======================
//Weapon::SetBulletDamage
//======================
void Weapon::SetBulletDamage
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletdamage[firemodeindex] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetBulletLarge
//======================
void Weapon::SetBulletLarge
(
Event* ev
)
{
2023-07-31 23:41:53 +02:00
bulletlarge[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::SetBulletLarge
//======================
void Weapon::SetTracerSpeed
(
Event* ev
)
{
2023-07-31 23:41:53 +02:00
tracerspeed[firemodeindex] = ev->GetFloat(1);
}
2016-03-27 11:49:47 +02:00
//======================
//Weapon::SetDMBulletDamage
//======================
void Weapon::SetDMBulletDamage
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletdamage[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetBulletKnockback
//======================
void Weapon::SetBulletKnockback
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletknockback[firemodeindex] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetDMBulletKnockback
//======================
void Weapon::SetDMBulletKnockback
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletknockback[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetBulletThroughWood
//======================
void Weapon::SetBulletThroughWood(Event* ev)
{
2023-07-31 23:41:53 +02:00
bulletthroughwood[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletThroughMetal
//======================
void Weapon::SetBulletThroughMetal(Event* ev)
{
2023-07-31 23:41:53 +02:00
bulletthroughmetal[firemodeindex] = ev->GetFloat(1);
}
2016-03-27 11:49:47 +02:00
//======================
//Weapon::SetBulletRange
//======================
void Weapon::SetBulletRange
(
Event *ev
)
{
if( g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer )
2016-03-27 11:49:47 +02:00
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletrange[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetDMBulletRange
//======================
void Weapon::SetDMBulletRange
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletrange[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetRange
//======================
void Weapon::SetRange
(
Event *ev
)
{
SetBulletRange( ev );
}
//======================
//Weapon::SetBulletCount
//======================
void Weapon::SetBulletCount
(
Event *ev
)
{
if( g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer )
2016-03-27 11:49:47 +02:00
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletcount[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetDMBulletCount
//======================
void Weapon::SetDMBulletCount
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletcount[ firemodeindex ] = ev->GetFloat( 1 );
}
//======================
//Weapon::SetBulletSpread
//======================
void Weapon::SetBulletSpread
(
Event *ev
)
{
if( g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer )
2016-03-27 11:49:47 +02:00
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletspread[ firemodeindex ].x = ev->GetFloat( 1 );
bulletspread[ firemodeindex ].y = ev->GetFloat( 2 );
if( ev->NumArgs() > 2 )
{
bulletspreadmax[ firemodeindex ].x = ev->GetFloat( 3 );
bulletspreadmax[ firemodeindex ].y = ev->GetFloat( 4 );
}
}
//======================
//Weapon::SetDMBulletSpread
//======================
void Weapon::SetDMBulletSpread
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
bulletspread[ firemodeindex ].x = ev->GetFloat( 1 );
bulletspread[ firemodeindex ].y = ev->GetFloat( 2 );
if( ev->NumArgs() > 2 )
{
bulletspreadmax[ firemodeindex ].x = ev->GetFloat( 3 );
bulletspreadmax[ firemodeindex ].y = ev->GetFloat( 4 );
}
}
//======================
//Weapon::SetZoomSpreadMult
//======================
void Weapon::SetZoomSpreadMult
(
Event *ev
)
{
if( g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer )
2016-03-27 11:49:47 +02:00
return;
m_fZoomSpreadMult = ev->GetFloat( 1 );
}
//======================
//Weapon::SetDMZoomSpreadMult
//======================
void Weapon::SetDMZoomSpreadMult
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
m_fZoomSpreadMult = ev->GetFloat( 1 );
}
//======================
//Weapon::SetZoomSpreadMult
//======================
void Weapon::SetFireSpreadMult
(
Event *ev
)
{
if( g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer )
2016-03-27 11:49:47 +02:00
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
m_fFireSpreadMultAmount[ firemodeindex ] = ev->GetFloat( 1 );
m_fFireSpreadMultFalloff[ firemodeindex ] = ev->GetFloat( 2 );
m_fFireSpreadMultCap[ firemodeindex ] = ev->GetFloat( 3 );
m_fFireSpreadMultTimeCap[ firemodeindex ] = ev->GetFloat( 4 );
}
//======================
//Weapon::SetDMZoomSpreadMult
//======================
void Weapon::SetDMFireSpreadMult
(
Event *ev
)
{
if( !g_gametype->integer )
return;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
m_fFireSpreadMultAmount[ firemodeindex ] = ev->GetFloat( 1 );
m_fFireSpreadMultFalloff[ firemodeindex ] = ev->GetFloat( 2 );
m_fFireSpreadMultCap[ firemodeindex ] = ev->GetFloat( 3 );
m_fFireSpreadMultTimeCap[ firemodeindex ] = ev->GetFloat( 4 );
}
//======================
//Weapon::SetTracerFrequency
//======================
void Weapon::SetTracerFrequency
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
tracerfrequency[ firemodeindex ] = ev->GetInteger( 1 );
}
//======================
//Weapon::Alternate
//======================
void Weapon::Secondary
(
Event *ev
)
{
int i;
Event *altev = new Event( ev->GetToken( 1 ) );
firemodeindex = FIRE_SECONDARY;
for( i = 2; i <= ev->NumArgs(); i++ )
{
altev->AddToken( ev->GetToken( i ) );
}
ProcessEvent( altev );
firemodeindex = FIRE_PRIMARY;
}
//====================
//Weapon::AutoAim
//====================
void Weapon::AutoAim
(
Event *ev
)
{
autoaim = true;
}
//====================
//Weapon::Crosshair
//====================
void Weapon::Crosshair
(
Event *ev
)
{
crosshair = ev->GetBoolean( 1 );
}
//====================
//Weapon::DMCrosshair
//====================
void Weapon::DMCrosshair
(
Event *ev
)
{
if( !g_gametype->integer )
return;
crosshair = ev->GetBoolean( 1 );
}
//====================
//Weapon::OffHandAttachToTag
//====================
void Weapon::OffHandAttachToTag
(
Event *ev
)
{
attachToTag_offhand = ev->GetString( 1 );
}
//====================
//Weapon::MainAttachToTag
//====================
void Weapon::MainAttachToTag
(
Event *ev
)
{
attachToTag_main = ev->GetString( 1 );
}
//====================
//Weapon::HolsterAttachToTag
//====================
void Weapon::HolsterAttachToTag
(
Event *ev
)
{
holster_attachToTag = ev->GetString( 1 );
}
//====================
//Weapon::SetHolsterOffset
//====================
void Weapon::SetHolsterOffset
(
Event *ev
)
{
holsterOffset = ev->GetVector( 1 );
}
//====================
//Weapon::SetHolsterAngles
//====================
void Weapon::SetHolsterAngles
(
Event *ev
)
{
holsterAngles = ev->GetVector( 1 );
}
//====================
//Weapon::SetHolsterScale
//====================
void Weapon::SetHolsterScale
(
Event *ev
)
{
holsterScale = ev->GetFloat( 1 );
}
//====================
//Weapon::SetQuiet
//====================
void Weapon::SetQuiet
(
Event *ev
)
{
quiet[ firemodeindex ] = true;
}
//====================
//Weapon::SetLoopFire
//====================
void Weapon::SetLoopFire
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
loopfire[firemodeindex] = true;
}
//====================
//Weapon::GetZoom
//====================
int Weapon::GetZoom( void )
{
return m_iZoom;
}
//====================
//Weapon::GetAutoZoom
//====================
qboolean Weapon::GetAutoZoom( void )
{
return m_bAutoZoom;
}
//======================
//Weapon::SetMeansOfDeath
//======================
void Weapon::SetMeansOfDeath
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
meansofdeath[firemodeindex] = (meansOfDeath_t )MOD_NameToNum( ev->GetString( 1 ) );
}
//======================
//Weapon::GetMeansOfDeath
//======================
meansOfDeath_t Weapon::GetMeansOfDeath
(
firemode_t mode
)
{
assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
if( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
return meansofdeath[ mode ];
else
{
warning( "Weapon::GetMeansOfDeath", "Invalid mode %d\n", mode );
return MOD_NONE;
}
}
//======================
//Weapon::SetAimTarget
//======================
void Weapon::SetAimTarget
(
Entity *ent
)
{
aim_target = ent;
}
qboolean Weapon::ShouldReload( void )
{
if( m_bShouldReload )
{
return qtrue;
}
else
{
if( ammo_clip_size[ FIRE_PRIMARY ] && !ammo_in_clip[ FIRE_PRIMARY ] && AmmoAvailable( FIRE_PRIMARY ) )
return qtrue;
}
return qfalse;
}
void Weapon::SetShouldReload( qboolean should_reload )
{
m_bShouldReload = should_reload;
}
void Weapon::StartReloading( void )
{
if( !ammo_clip_size[ 0 ] || !owner ) {
return;
}
2018-09-17 23:50:38 +02:00
Event ev( EV_Weapon_DoneReloading );
2016-03-27 11:49:47 +02:00
if( SetWeaponAnim( "reload", ev ) )
{
weaponstate = WEAPON_RELOADING;
}
else
{
ProcessEvent( EV_Weapon_FillClip );
ProcessEvent( EV_Weapon_DoneReloading );
}
m_fFireSpreadMult[ FIRE_PRIMARY ] = 0;
}
//======================
//Weapon::WorldHitSpawn
//======================
void Weapon::WorldHitSpawn
(
firemode_t mode,
Vector origin,
Vector angles,
float life
)
{
}
//======================
//Weapon::SetWorldHitSpawn
//======================
void Weapon::SetWorldHitSpawn
(
Event *ev
)
{
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
worldhitspawn[firemodeindex] = ev->GetString( 1 );
}
void Weapon::SetWeaponGroup( Event *ev )
{
m_csWeaponGroup = ev->GetConstString( 1 );
}
void Weapon::SetZoom( Event *ev )
{
m_iZoom = ev->GetInteger( 1 );
if( ev->NumArgs() > 1 )
{
int autozoom = ev->GetInteger( 2 );
if( autozoom ) {
m_bAutoZoom = qtrue;
}
}
}
void Weapon::SetSemiAuto( Event *ev )
{
m_bSemiAuto = qtrue;
}
void Weapon::SetWeaponType( Event *ev )
{
weapon_class = G_WeaponClassNameToNum( ev->GetString( 1 ) );
}
void Weapon::EventClipAdd( Event *ev )
{
int amount;
int amount_used;
// Calc the amount the clip should get
amount = ev->GetInteger( 1 );
assert( owner );
if( owner && owner->isClient() && !UnlimitedAmmo( FIRE_PRIMARY ) )
{
if( amount > ammo_clip_size[ FIRE_PRIMARY ] )
amount = ammo_clip_size[ FIRE_PRIMARY ] - ammo_in_clip[ FIRE_PRIMARY ];
// use up the ammo from the player
amount_used = owner->UseAmmo( ammo_type[ FIRE_PRIMARY ], amount );
// Stick it in the clip
if( ammo_clip_size[ FIRE_PRIMARY ] )
ammo_in_clip[ FIRE_PRIMARY ] = amount_used + ammo_in_clip[ FIRE_PRIMARY ];
if( ammo_in_clip[ FIRE_PRIMARY ] > ammo_clip_size[ FIRE_PRIMARY ] )
ammo_in_clip[ FIRE_PRIMARY ] = ammo_clip_size[ FIRE_PRIMARY ];
owner->AmmoAmountInClipChanged( ammo_type[ FIRE_PRIMARY ], ammo_in_clip[ FIRE_PRIMARY ] );
SetShouldReload( qfalse );
}
}
void Weapon::EventClipEmpty( Event *ev )
{
if( !ammo_clip_size[ FIRE_PRIMARY ] )
return;
if( !owner )
return;
owner->GiveAmmo( ammo_type[ FIRE_PRIMARY ], ammo_in_clip[ FIRE_PRIMARY ] );
ammo_in_clip[ FIRE_PRIMARY ] = 0;
SetShouldReload( qtrue );
owner->AmmoAmountInClipChanged( ammo_type[ FIRE_PRIMARY ], ammo_in_clip[ FIRE_PRIMARY ] );
}
void Weapon::EventClipFill( Event *ev )
{
int amount;
int amount_used;
// Calc the amount the clip should get
amount = ammo_clip_size[ FIRE_PRIMARY ] - ammo_in_clip[ FIRE_PRIMARY ];
assert( owner );
if( owner && owner->isClient() && !UnlimitedAmmo( FIRE_PRIMARY ) )
{
// use up the ammo from the player
amount_used = owner->UseAmmo( ammo_type[ FIRE_PRIMARY ], amount );
// Stick it in the clip
if( ammo_clip_size[ FIRE_PRIMARY ] )
ammo_in_clip[ FIRE_PRIMARY ] = amount_used + ammo_in_clip[ FIRE_PRIMARY ];
assert( ammo_in_clip[ FIRE_PRIMARY ] <= ammo_clip_size[ FIRE_PRIMARY ] );
if( ammo_in_clip[ FIRE_PRIMARY ] > ammo_clip_size[ FIRE_PRIMARY ] )
ammo_in_clip[ FIRE_PRIMARY ] = ammo_clip_size[ FIRE_PRIMARY ];
}
owner->AmmoAmountInClipChanged( ammo_type[ FIRE_PRIMARY ], ammo_in_clip[ FIRE_PRIMARY ] );
SetShouldReload( qfalse );
}
float Weapon::FireDelay
(
firemode_t mode
)
{
2023-07-31 23:41:53 +02:00
return fire_delay[ mode ];
2016-03-27 11:49:47 +02:00
}
void Weapon::EventSetFireDelay
(
Event *ev
)
{
if( g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer )
2016-03-27 11:49:47 +02:00
return;
2023-07-31 23:41:53 +02:00
fire_delay[ firemodeindex ] = ev->GetFloat( 1 );
2016-03-27 11:49:47 +02:00
}
void Weapon::EventSetDMFireDelay
(
Event *ev
)
{
if( !g_gametype->integer )
return;
2023-07-31 23:41:53 +02:00
fire_delay[ firemodeindex ] = ev->GetFloat( 1 );
2016-03-27 11:49:47 +02:00
}
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 ) )
{
2019-06-29 23:43:30 +02:00
BroadcastAIEvent(AI_EVENT_MISC, radius );
2016-03-27 11:49:47 +02:00
next_noise_time = level.time + 1;
}
}
void Weapon::FallingAngleAdjust
(
Event *ev
)
{
Vector vTmp;
Vector vDir;
trace_t trace;
if( owner )
return;
if( groundentity && ( groundentity->entity == world || groundentity->r.bmodel ) )
{
Vector vEnd = origin - Vector( 0, 0, 128 );
trace = G_Trace( origin,
vec_zero,
vec_zero,
vEnd,
last_owner,
edict->clipmask,
qfalse,
"Weapon::FallingAngleAdjust" );
if( trace.fraction < 1.0f )
{
Vector vAng = Vector( 0, angles[ 1 ], 0 );
Vector vCross;
/*vAng.AngleVectorsLeft( &vDir );
CrossProduct( vAng, vDir, vCross );
CrossProduct( vDir, vCross, vAng );
vAng = vAng.toAngles();*/
if( angles[ 2 ] <= 0.0f )
vAng[ 2 ] = anglemod( vAng[ 2 ] - 90.0f );
else
vAng[ 2 ] = anglemod( vAng[ 2 ] + 90.0f );
setAngles( vAng );
}
if( weapon_class >= 0 )
{
Sound( G_WeaponClassNumToName( weapon_class ) + "_drop" );
}
return;
}
angles[ 0 ] = anglemod( angles[ 0 ] );
//if( angles[ 0 ] > 180.0f )
// angles[ 0 ] -= 180.0f;
if( angles[ 0 ] >= -90.0f && angles[ 0 ] <= 0.0f )
{
angles[ 0 ] -= level.frametime * 160.0f;
if( angles[ 0 ] < -180.0f )
angles[ 0 ] = -180.0f;
}
else if( angles[ 0 ] > 0.0f )
{
angles[ 0 ] -= level.frametime * 160.0f;
}
else
{
angles[ 0 ] += level.frametime * 160.0f;
}
angles[ 2 ] = anglemod( angles[ 2 ] );
//if( angles[ 2 ] > 180.0f )
// angles[ 2 ] -= 180.0f;
if( angles[ 2 ] > -90.0f && angles[ 2 ] < 0.0f )
{
angles[ 2 ] -= level.frametime * 160.0f;
}
else if( angles[ 2 ] > 90.0f )
{
angles[ 2 ] -= level.frametime * 160.0f;
}
else
{
angles[ 2 ] += level.frametime * 160.0f;
}
angles[ 0 ] = anglemod( angles[ 0 ] );
angles[ 2 ] = anglemod( angles[ 2 ] );
setAngles( angles );
PostEvent( EV_Weapon_FallingAngleAdjust, level.frametime );
}
qboolean Weapon::GetUseCrosshair() const
{
if (m_fMaxFireMovement >= 1.f) {
return crosshair;
}
if (!owner || (owner->velocity.lengthXY() / sv_runspeed->value) <= (m_fMaxFireMovement * m_fMovementSpeed)) {
return crosshair;
}
return qfalse;
}
2016-03-27 11:49:47 +02:00
void Weapon::SetAIRange
(
Event *ev
)
{
str s = ev->GetString( 1 );
if( !s.icmp( "short" ) )
{
mAIRange = RANGE_SHORT;
}
else if( !s.icmp( "medium" ) )
{
mAIRange = RANGE_MEDIUM;
}
else if( !s.icmp( "long" ) )
{
mAIRange = RANGE_LONG;
}
else if( !s.icmp( "sniper" ) )
{
mAIRange = RANGE_SNIPER;
}
else
{
warning( "Weapon::SetAIRange", "unknown range: %s. Should be short, medium, long, or sniper\n", s.c_str() );
}
}
void Weapon::SetViewKick
(
Event *ev
)
{
float pitchmin, pitchmax;
float yawmin, yawmax;
assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
pitchmin = ev->GetFloat( 1 );
pitchmax = ev->GetFloat( 2 );
if( pitchmin <= pitchmax )
{
viewkickmin[ firemodeindex ][ 0 ] = pitchmin;
viewkickmax[ firemodeindex ][ 0 ] = pitchmax;
}
else
{
viewkickmin[ firemodeindex ][ 0 ] = pitchmax;
viewkickmax[ firemodeindex ][ 0 ] = pitchmin;
}
if( ev->NumArgs() > 2 )
{
yawmin = ev->GetFloat( 3 );
yawmax = ev->GetFloat( 4 );
if( pitchmin <= pitchmax )
{
viewkickmin[ firemodeindex ][ 1 ] = yawmin;
viewkickmax[ firemodeindex ][ 1 ] = yawmax;
}
else
{
viewkickmin[ firemodeindex ][ 1 ] = yawmax;
viewkickmax[ firemodeindex ][ 1 ] = yawmin;
}
}
}
void Weapon::SetMovementSpeed
(
Event *ev
)
{
if( g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer )
2016-03-27 11:49:47 +02:00
return;
m_fMovementSpeed = ev->GetFloat( 1 );
}
void Weapon::SetDMMovementSpeed
(
Event *ev
)
{
if( !g_gametype->integer )
return;
m_fMovementSpeed = ev->GetFloat( 1 );
}
void Weapon::SetMaxFireMovement(Event* ev)
{
2023-07-31 23:41:53 +02:00
m_fMaxFireMovement = ev->GetFloat(1);
if (m_fMaxFireMovement > 1.0) {
// Cap at 1.0
m_fMaxFireMovement = 1.0;
}
}
void Weapon::SetZoomMovement(Event* ev)
{
2023-07-31 23:41:53 +02:00
m_fZoomMovement = ev->GetFloat(1);
if (m_fZoomMovement > 1.0) {
// Cap at 1.0
m_fZoomMovement = 1.0;
}
}
2016-03-27 11:49:47 +02:00
void Weapon::EventAmmoPickupSound
(
Event *ev
)
{
m_sAmmoPickupSound = ev->GetString( 1 );
}
void Weapon::EventNoAmmoSound
(
Event *ev
)
{
m_NoAmmoSound = ev->GetString( 1 );
}
void Weapon::EventMaxMovementSound(Event* ev)
{
2023-07-31 23:41:53 +02:00
m_sMaxMovementSound = ev->GetString(1);
}
void Weapon::SetNumFireAnims(Event* ev)
{
2023-07-31 23:41:53 +02:00
m_iNumFireAnims = ev->GetInteger(1);
}
void Weapon::SetWeaponSubtype(Event* ev)
{
2023-07-31 23:41:53 +02:00
m_iWeaponSubtype = ev->GetInteger(1);
}
void Weapon::SetCookTime(Event* ev)
{
2023-07-31 23:41:53 +02:00
m_fCookTime = ev->GetFloat(1);
}
void Weapon::SetCurrentFireAnim(Event* ev)
{
2023-07-31 23:41:53 +02:00
m_iCurrentFireAnim = ev->GetInteger(1);
}
void Weapon::SetSecondaryAmmoInHud(Event* ev)
{
2023-07-31 23:41:53 +02:00
m_bSecondaryAmmoInHud = qtrue;
}
2016-03-27 11:49:47 +02:00
float Weapon::GetBulletRange
(
firemode_t mode
)
{
return bulletrange[ mode ];
}