Files
bearstonem--openmohaa-vr/code/fgame/weapon.cpp
T
bearstonemandClaude Opus 5 046e2dd9a6 feat(vr): put the weapon in the player's hand, from the reference
Four pieces, each adapted from RTCWQuest rather than invented.

Aim. Weapon::GetMuzzlePosition derives a shot from player->m_vViewAng, which is
MOHAA's analogue of rtcw g_weapon.c:1904. It now takes the controller's pitch
and the yaw the hand leads the head by, added to the view yaw the game already
has - the controller's absolute yaw means nothing to the game, since the player
can snap turn on the stick and the play space has an arbitrary forward. Only the
trace moves; the camera stays on the head.

The angles reach fgame through gameImport_t rather than the usercmd, which has
no spare field - angles[3] is the view direction and buttons is packed. A
dedicated server answers false and keeps the aim it had.

Placement. The first person model is MOHAA's FPS view model, positioned by
CG_OffsetFirstPersonView against the camera and given the walk bob. Both are
wrong in a headset: the hand has its own position, and shaking the view model
when the camera is the player's head is a way to make people ill. It now goes
where the hand is - RTCWQuest's CG_CalculateVRWeaponPosition, including
dropping the origin to the feet and adding the measured head height back so the
weapon hangs at the height the player is really holding it, rather than off the
camera where crouching would drag it about.

Selection. Nothing was bound to it at all, and there is no button left - the
triggers fire, both face buttons on each hand are objectives and jump, and the
sticks and grips are duck and use. The reference does not spend a button either:
the dominant thumbstick up is weapprev and down is weapnext, gated on the
sideways component so it cannot be read as a turn and edge triggered so a held
stick does not run the inventory. This port's turn already ignores that stick's
Y axis, so nothing was displaced.

Two handed hold. Off hand grip squeezed with the hands close together points
the weapon along the line between them instead of along the weapon hand alone -
how a rifle is actually held, and steadier, because a wobble at one hand is
damped by the distance to the other. Roll is halved rather than taken from the
line, which carries none. The off hand grip was a duplicate of use; the weapon
hand's still opens doors.

The suggested binding array was 16 with 14 in use. Now 32.

Aim and placement are built but not yet confirmed on the device.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 08:10:24 -07:00

4382 lines
121 KiB
C++

/*
===========================================================================
Copyright (C) 2025 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// weapon.cpp: Source file for Weapon class. The weapon class is the base class for
// all weapons in Sin. Any entity created from a class derived from the weapon
// class will be usable by any Sentient (players and monsters) as a weapon.
//
#include "g_local.h"
#include "g_phys.h"
#include "entity.h"
#include "item.h"
#include "weapon.h"
#include "scriptmaster.h"
#include "sentient.h"
#include "misc.h"
#include "specialfx.h"
#include "actor.h"
#include "weaputils.h"
#include "player.h"
#include "vehicleturret.h"
#include "debuglines.h"
#include "g_spawn.h"
Event EV_Weapon_Shoot("shoot", EV_DEFAULT, "S", "mode", "Shoot the weapon", EV_NORMAL);
Event EV_Weapon_DoneRaising(
"ready", EV_DEFAULT, NULL, NULL, "Signals the end of the ready animation so the weapon can be used", EV_NORMAL
);
Event EV_Weapon_DoneFiring("donefiring", EV_DEFAULT, NULL, NULL, "Signals the end of the fire animation", EV_NORMAL);
Event EV_Weapon_Idle("idle", EV_DEFAULT, NULL, NULL, "Puts the weapon into an idle state", EV_NORMAL);
Event EV_Weapon_IdleInit(
"idleinit", EV_DEFAULT, NULL, NULL, "Puts the weapon into an idle state and clears all the anim slots", EV_NORMAL
);
Event EV_Weapon_SecondaryUse(
"secondaryuse", EV_DEFAULT, NULL, NULL, "Puts the weapon into its secondary mode of operation", EV_NORMAL
);
Event EV_Weapon_DoneReloading(
"donereloading", EV_DEFAULT, NULL, NULL, "Signals the end of the reload animation", EV_NORMAL
);
Event EV_Weapon_SetAmmoClipSize(
"clipsize", EV_DEFAULT, "i", "ammoClipSize", "Set the amount of rounds a clip of the weapon holds", EV_NORMAL
);
Event EV_Weapon_SetAmmoInClip(
"ammo_in_clip", EV_DEFAULT, "i", "ammoInClip", "Set the amount of ammo in the clip", EV_NORMAL
);
Event EV_Weapon_SetShareClip(
"shareclip",
EV_DEFAULT,
"i",
"shareClip",
"Sets the weapon to share the same clip between all fire modes",
EV_NORMAL
);
Event EV_Weapon_FillClip(
"clip_fill", EV_DEFAULT, NULL, NULL, "Fills the weapons ammo clip with ammo from its owner", EV_NORMAL
);
Event EV_Weapon_EmptyClip(
"clip_empty",
EV_DEFAULT,
NULL,
NULL,
"Empties the weapon's clip of ammo,"
"returning it to the owner",
EV_NORMAL
);
Event EV_Weapon_AddToClip(
"clip_add", EV_DEFAULT, "i", "ammoAmount", "Add to the weapons ammo clip with ammo from its owner", EV_NORMAL
);
Event EV_Weapon_SetMaxRange(
"maxrange",
EV_DEFAULT,
"f",
"maxRange",
"Set the maximum range of a weapon so the AI knows how to use it",
EV_NORMAL
);
Event EV_Weapon_SetMinRange(
"minrange",
EV_DEFAULT,
"f",
"minRange",
"Set the minimum range of a weapon so the AI knows how to use it",
EV_NORMAL
);
Event EV_Weapon_FireDelay(
"firedelay", EV_DEFAULT, "f", "fFireDelay", "Set the minimum time between shots from the weapon", EV_NORMAL
);
Event EV_Weapon_DMSetFireDelay(
"dmfiredelay", EV_DEFAULT, "f", "fFireDelay", "Set the minimum time between shots from the weapon", EV_NORMAL
);
Event EV_Weapon_NotDroppable("notdroppable", EV_DEFAULT, NULL, NULL, "Makes a weapon not droppable");
Event EV_Weapon_SetAimAnim(
"setaimanim",
EV_DEFAULT,
"si",
"aimAnimation aimFrame",
"Set the aim animation and frame for when a weapon fires",
EV_NORMAL
);
Event
EV_Weapon_SetRank("rank", EV_DEFAULT, "ii", "iOrder iRank", "Set the order value and power ranking for the weapon");
Event EV_Weapon_SetFireType(
"firetype", EV_DEFAULT, "s", "firingType", "Set the firing type of the weapon (projectile or bullet)", EV_NORMAL
);
Event EV_Weapon_SetAIRange(
"airange",
EV_DEFAULT,
"s",
"airange",
"Set the range of this gun for the ai: short, medium, long, sniper",
EV_NORMAL
);
Event EV_Weapon_SetProjectile(
"projectile",
EV_DEFAULT,
"s",
"projectileModel",
"Set the model of the projectile that this weapon fires",
EV_NORMAL
);
Event EV_Weapon_SetDMProjectile(
"dmprojectile",
EV_DEFAULT,
"s",
"projectileModel",
"Set the model of the projectile that this weapon fires",
EV_NORMAL
);
Event EV_Weapon_SetBulletDamage(
"bulletdamage", EV_DEFAULT, "f", "bulletDamage", "Set the damage that the bullet causes", EV_NORMAL
);
Event EV_Weapon_SetDMBulletDamage(
"dmbulletdamage", EV_DEFAULT, "f", "bulletDamage", "Set the damage that the bullet causes", EV_NORMAL
);
Event EV_Weapon_SetBulletLarge(
"bulletlarge",
EV_DEFAULT,
"i",
"bulletType",
"Set if the bullets fired are rifle bullets(1), artillery(2) or larger tracers(3)",
EV_NORMAL
);
Event EV_Weapon_SetTracerSpeed(
"tracerspeed",
EV_DEFAULT,
"f",
"speed",
"Scale factor of how fast a tracer should travel (valid ranges 0-2)",
EV_NORMAL
);
Event EV_Weapon_SetBulletKnockback(
"bulletknockback", EV_DEFAULT, "f", "bulletKnockback", "Set the knockback that the bullet causes", EV_NORMAL
);
Event EV_Weapon_SetDMBulletKnockback(
"dmbulletknockback", EV_DEFAULT, "f", "bulletKnockback", "Set the knockback that the bullet causes", EV_NORMAL
);
Event EV_Weapon_SetBulletThroughWood(
"throughwood", EV_DEFAULT, "f", "dist", "Sets how far the bullets can go through wood", EV_NORMAL
);
Event EV_Weapon_SetBulletThroughMetal(
"throughmetal", EV_DEFAULT, "f", "dist", "Sets how far the bullets can go through metal", EV_NORMAL
);
Event EV_Weapon_SetBulletCount(
"bulletcount", EV_DEFAULT, "f", "bulletCount", "Set the number of bullets this weapon shoots when fired", EV_NORMAL
);
Event EV_Weapon_SetDMBulletCount(
"dmbulletcount",
EV_DEFAULT,
"f",
"bulletCount",
"Set the number of bullets this weapon shoots when fired",
EV_NORMAL
);
Event
EV_Weapon_SetBulletRange("bulletrange", EV_DEFAULT, "f", "bulletRange", "Set the range of the bullets", EV_NORMAL);
Event EV_Weapon_SetDMBulletRange(
"dmbulletrange", EV_DEFAULT, "f", "bulletRange", "Set the range of the bullets", EV_NORMAL
);
Event EV_Weapon_SetBulletSpread(
"bulletspread",
EV_DEFAULT,
"ffFF",
"bulletSpreadX bulletSpreadY bulletSpreadXmax bulletSpreadYmax",
"Set the min & optional max spread of the bullet in the x and y axis",
EV_NORMAL
);
Event EV_Weapon_SetDMBulletSpread(
"dmbulletspread",
EV_DEFAULT,
"ffFF",
"bulletSpreadX bulletSpreadY bulletSpreadXmax bulletSpreadYmax",
"Set the min & optional max spread of the bullet in the x and y axis",
EV_NORMAL
);
Event EV_Weapon_SetZoomSpreadMult(
"zoomspreadmult",
EV_DEFAULT,
"f",
"scale",
"Sets the spread multiplier for when using the zoom on a zooming weapon",
EV_NORMAL
);
Event EV_Weapon_SetDMZoomSpreadMult(
"dmzoomspreadmult",
EV_DEFAULT,
"f",
"scale",
"Sets the spread multiplier for when using the zoom on a zooming weapon",
EV_NORMAL
);
Event EV_Weapon_SetFireSpreadMult(
"firespreadmult",
EV_DEFAULT,
"ffff",
"scaleadd falloff cap maxtime",
"Sets a time decayed multiplyer to spread when the weapon is fired",
EV_NORMAL
);
Event EV_Weapon_SetDMFireSpreadMult(
"dmfirespreadmult",
EV_DEFAULT,
"ffff",
"scaleadd falloff cap maxtime",
"Sets a time decayed multiplyer to spread when the weapon is fired",
EV_NORMAL
);
Event EV_Weapon_SetTracerFrequency(
"tracerfrequency", EV_DEFAULT, "f", "frequenct", "Set the frequency of making tracers", EV_NORMAL
);
Event EV_Weapon_SetRange("range", EV_DEFAULT, "f", "range", "Set the range of the weapon", EV_NORMAL);
Event EV_Weapon_Secondary(
"secondary",
EV_DEFAULT,
"SSSSSSSS",
"arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8",
"Set the secondary mode of the weapon, by passing commands through",
EV_NORMAL
);
Event EV_Weapon_AmmoType("ammotype", EV_DEFAULT, "s", "name", "Set the type of ammo this weapon uses", EV_NORMAL);
Event EV_Weapon_StartAmmo("startammo", EV_DEFAULT, "i", "amount", "Set the starting ammo of this weapon", EV_NORMAL);
Event
EV_Weapon_DMStartAmmo("dmstartammo", EV_DEFAULT, "i", "amount", "Set the starting ammo of this weapon", EV_NORMAL);
Event EV_Weapon_AmmoRequired(
"ammorequired", EV_DEFAULT, "i", "amount", "Set the amount of ammo this weapon requires to fire", EV_NORMAL
);
Event EV_Weapon_DMAmmoRequired(
"dmammorequired", EV_DEFAULT, "i", "amount", "Set the amount of ammo this weapon requires to fire", EV_NORMAL
);
Event EV_Weapon_MaxChargeTime(
"maxchargetime", EV_DEFAULT, "i", "time", "Set the maximum time the weapon may be charged up", EV_NORMAL
);
Event EV_Weapon_MinChargeTime(
"minchargetime", EV_DEFAULT, "i", "time", "Set the minimum time the weapon must be charged up", EV_NORMAL
);
Event EV_Weapon_AddAdditionalStartAmmo(
"additionalstartammo",
EV_DEFAULT,
"si",
"ammotype amount",
"Gives some additional start ammo of the specified type",
EV_NORMAL
);
Event EV_Weapon_AddStartItem(
"startitem", EV_DEFAULT, "s", "itemname", "Adds an item to the starting loadout of the weapon", EV_NORMAL
);
Event EV_Weapon_GiveStartingAmmo(
"startingammotoowner",
EV_DEFAULT,
NULL,
NULL,
"Internal event used to give ammo to the owner of the weapon",
EV_NORMAL
);
Event EV_Weapon_AutoAim("autoaim", EV_DEFAULT, NULL, NULL, "Turn on auto aiming for the weapon", EV_NORMAL);
Event EV_Weapon_Crosshair("crosshair", EV_DEFAULT, "b", "bool", "Turn on/off the crosshair for this weapon", EV_NORMAL);
Event EV_Weapon_DMCrosshair(
"dmcrosshair", EV_DEFAULT, "b", "bool", "Turn on/off the crosshair for this weapon", EV_NORMAL
);
Event EV_Weapon_SetQuiet("quiet", EV_DEFAULT, NULL, NULL, "Makes the weapon make no noise.", EV_NORMAL);
Event EV_Weapon_SetSecondaryAmmoInHud(
"secondaryammoinhud", EV_DEFAULT, NULL, NULL, "Makes the weapon show its secondary ammo to the hud.", EV_NORMAL
);
Event EV_Weapon_SetLoopFire(
"loopfire", EV_DEFAULT, NULL, NULL, "Makes the weapon fire by looping the fire animation.", EV_NORMAL
);
Event EV_Weapon_OffHandAttachToTag(
"offhandattachtotag",
EV_DEFAULT,
"s",
"tagname",
"Set the name of the tag to attach this to it's owner's off hand.",
EV_NORMAL
);
Event EV_Weapon_MainAttachToTag(
"mainattachtotag",
EV_DEFAULT,
"s",
"tagname",
"Set the name of the tag to attach this to it's owner when being used.",
EV_NORMAL
);
Event EV_Weapon_HolsterTag(
"holstertag",
EV_DEFAULT,
"s",
"tagname",
"Set the name of the tag to attach this to when the weapon is holstered.",
EV_NORMAL
);
Event EV_Weapon_HolsterOffset(
"holsteroffset", EV_DEFAULT, "v", "offset", "Set the positional offset when it is holstered", EV_NORMAL
);
Event EV_Weapon_HolsterAngles(
"holsterangles", EV_DEFAULT, "v", "angles", "Set the angles of this weapon when it is holstered", EV_NORMAL
);
Event EV_Weapon_HolsterScale(
"holsterscale", EV_DEFAULT, "f", "scale", "Set the scale of this weapon when it is holstered", EV_NORMAL
);
Event EV_Weapon_AutoPutaway(
"autoputaway", EV_DEFAULT, "b", "bool", "Set the weapon to be automatically put away when out of ammo", EV_NORMAL
);
Event EV_Weapon_UseNoAmmo(
"usenoammo", EV_DEFAULT, "b", "bool", "Set the weapon to be able to be used when it's out of ammo", EV_NORMAL
);
Event EV_Weapon_SetMeansOfDeath(
"meansofdeath", EV_DEFAULT, "s", "meansOfDeath", "Set the meansOfDeath of the weapon.", EV_NORMAL
);
Event EV_Weapon_SetWorldHitSpawn(
"worldhitspawn",
EV_DEFAULT,
"s",
"modelname",
"Set a model to be spawned when the weapon strikes the world->",
EV_NORMAL
);
Event EV_Weapon_MakeNoise(
"makenoise", EV_DEFAULT, "FB", "noise_radius force", "Makes the weapon make noise that actors can hear.", EV_NORMAL
);
Event EV_Weapon_SetType("weapontype", EV_DEFAULT, "s", "weapon_type", "Sets the weapon type", EV_NORMAL);
Event EV_Weapon_SetGroup(
"weapongroup",
EV_DEFAULT,
"s",
"weapon_group",
"Sets the weapon group, a set of animations for actor animations scripts to use",
EV_NORMAL
);
Event EV_Weapon_SetZoom(
"zoom", EV_DEFAULT, "iI", "zoomfov autozoom", "Sets fov to zoom to on a secondary fire", EV_NORMAL
);
Event EV_Weapon_SetSemiAuto("semiauto", EV_DEFAULT, NULL, NULL, "Sets the weapon to fire semi-auto", EV_NORMAL);
Event EV_Weapon_AttachToHand(
"attachtohand", EV_DEFAULT, "s", "weapon_hand", "Attaches an active weapon to the specified hand", EV_NORMAL
);
Event EV_Weapon_CantPartialReload(
"cantpartialreload",
EV_DEFAULT,
NULL,
NULL,
"Prevents the weapon from being reloaded part way through a clip",
EV_NORMAL
);
Event EV_Weapon_DMCantPartialReload(
"dmcantpartialreload",
EV_DEFAULT,
NULL,
NULL,
"Prevents the weapon from being reloaded part way through a clip for DM",
EV_NORMAL
);
Event EV_Weapon_FallingAngleAdjust(
"fallingangleadjust", EV_DEFAULT, 0, 0, "Adjusts the weapons angles as it falls to the ground", EV_NORMAL
);
Event EV_Weapon_SetViewKick(
"viewkick",
EV_DEFAULT,
"ffFF",
"pitchmin pitchmax yawmin yawmax",
"Adds kick to the view of the owner when fired.",
EV_NORMAL
);
Event EV_Weapon_MovementSpeed(
"movementspeed",
EV_DEFAULT,
"f",
"speedmult",
"Alters the movement speed of the player when he has the weapon out",
EV_NORMAL
);
Event EV_Weapon_DMMovementSpeed(
"dmmovementspeed",
EV_DEFAULT,
"f",
"speedmult",
"Alters the movement speed of the player when he has the weapon out",
EV_NORMAL
);
Event EV_Weapon_MaxFireMovement(
"maxfiremovement",
EV_DEFAULT,
"f",
"speedmult",
"Sets the max speed the player can be moving to fire the weapon (fraction of weapon's running speed)",
EV_NORMAL
);
Event EV_Weapon_ZoomMovement(
"zoommovement",
EV_DEFAULT,
"f",
"speedmult",
"Sets the max speed the player can move while zoomed (fraction of weapon's running speed)",
EV_NORMAL
);
Event EV_Weapon_AmmoPickupSound(
"ammopickupsound", EV_DEFAULT, "s", "name", "sets the weapon's ammo pickup sound alias", EV_NORMAL
);
Event
EV_Weapon_NoAmmoSound("noammosound", EV_DEFAULT, "s", "name", "sets the weapon's dry fire sound alias", EV_NORMAL);
Event EV_Weapon_MaxMovementSound(
"maxmovementsound", EV_DEFAULT, "s", "name", "sets the weapon's movement fire prevention sound alias", EV_NORMAL
);
Event EV_Weapon_NumFireAnims(
"numfireanims", EV_DEFAULT, "i", "value", "Sets the number of fire animations this weapon uses.", EV_NORMAL
);
Event EV_Weapon_SetCurrentFireAnim(
"setcurrentfireanim", EV_DEFAULT, "i", "value", "Sets the current firing animation.", EV_NORMAL
);
Event EV_Weapon_SubType(
"weaponsubtype", EV_DEFAULT, "i", "subtype", "sets the weapon's sub-type. Used by smoke grenades.", EV_NORMAL
);
Event EV_SetCookTime("cooktime", EV_DEFAULT, "f", "cooktime", "sets weapons cook time.", EV_NORMAL);
Event EV_OverCooked(
"overcooked", EV_DEFAULT, NULL, NULL, "used when the cookable weapon has been over cooked.", EV_NORMAL
);
Event EV_OverCooked_Warning(
"overcooked_warning",
EV_DEFAULT,
NULL,
NULL,
"causes a warning sound to play that the grenade is about to be overcooked.",
EV_NORMAL
);
//
// Added in OPM
//
Event EV_Weapon_DoneAnimating("doneanimating", EV_DEFAULT, NULL, NULL, "Signals the end of animation", EV_NORMAL);
Event EV_Weapon_GetPutaway(
"putaway", EV_DEFAULT, NULL, NULL, "Returns whether or not the given weapon is being put away", EV_GETTER
);
CLASS_DECLARATION(Item, Weapon, NULL) {
{&EV_SetAnim, &Weapon::SetWeaponAnimEvent },
{&EV_Item_Pickup, &Weapon::PickupWeapon },
{&EV_Weapon_DoneRaising, &Weapon::DoneRaising },
{&EV_Weapon_DoneFiring, &Weapon::DoneFiring },
{&EV_Weapon_Idle, &Weapon::Idle },
{&EV_Weapon_IdleInit, &Weapon::IdleInit },
{&EV_BroadcastAIEvent, &Weapon::WeaponSound },
{&EV_Weapon_DoneReloading, &Weapon::DoneReloading },
{&EV_Weapon_SetAmmoClipSize, &Weapon::SetAmmoClipSize },
{&EV_Weapon_SetAmmoInClip, &Weapon::SetAmmoInClip },
{&EV_Weapon_SetShareClip, &Weapon::SetShareClip },
{&EV_Weapon_FillClip, &Weapon::FillAmmoClip },
{&EV_Weapon_EmptyClip, &Weapon::EmptyAmmoClip },
{&EV_Weapon_AddToClip, &Weapon::AddToAmmoClip },
{&EV_Weapon_SetMaxRange, &Weapon::SetMaxRangeEvent },
{&EV_Weapon_SetMinRange, &Weapon::SetMinRangeEvent },
{&EV_Weapon_FireDelay, &Weapon::SetFireDelay },
{&EV_Weapon_NotDroppable, &Weapon::NotDroppableEvent },
{&EV_Weapon_SetAimAnim, &Weapon::SetAimAnim },
{&EV_Weapon_Shoot, &Weapon::Shoot },
{&EV_Weapon_SetRank, &Weapon::SetRankEvent },
{&EV_Weapon_SetFireType, &Weapon::SetFireType },
{&EV_Weapon_SetAIRange, &Weapon::SetAIRange },
{&EV_Weapon_SetProjectile, &Weapon::SetProjectile },
{&EV_Weapon_SetDMProjectile, &Weapon::SetDMProjectile },
{&EV_Weapon_SetBulletDamage, &Weapon::SetBulletDamage },
{&EV_Weapon_SetBulletLarge, &Weapon::SetBulletLarge },
{&EV_Weapon_SetTracerSpeed, &Weapon::SetTracerSpeed },
{&EV_Weapon_SetBulletCount, &Weapon::SetBulletCount },
{&EV_Weapon_SetBulletKnockback, &Weapon::SetBulletKnockback },
{&EV_Weapon_SetBulletThroughWood, &Weapon::SetBulletThroughWood },
{&EV_Weapon_SetBulletThroughMetal, &Weapon::SetBulletThroughMetal },
{&EV_Weapon_SetBulletRange, &Weapon::SetBulletRange },
{&EV_Weapon_SetRange, &Weapon::SetRange },
{&EV_Weapon_SetBulletSpread, &Weapon::SetBulletSpread },
{&EV_Weapon_SetTracerFrequency, &Weapon::SetTracerFrequency },
{&EV_Weapon_Secondary, &Weapon::Secondary },
{&EV_Weapon_AmmoType, &Weapon::SetAmmoType },
{&EV_Weapon_StartAmmo, &Weapon::SetStartAmmo },
{&EV_Weapon_AmmoRequired, &Weapon::SetAmmoRequired },
{&EV_Weapon_MaxChargeTime, &Weapon::SetMaxChargeTime },
{&EV_Weapon_MinChargeTime, &Weapon::SetMinChargeTime },
{&EV_Weapon_AddAdditionalStartAmmo, &Weapon::AddAdditionalStartAmmo },
{&EV_Weapon_AddStartItem, &Weapon::AddStartItem },
{&EV_Weapon_GiveStartingAmmo, &Weapon::GiveStartingAmmoToOwner},
{&EV_Weapon_AutoAim, &Weapon::AutoAim },
{&EV_Weapon_Crosshair, &Weapon::Crosshair },
{&EV_Weapon_MainAttachToTag, &Weapon::MainAttachToTag },
{&EV_Weapon_OffHandAttachToTag, &Weapon::OffHandAttachToTag },
{&EV_Weapon_HolsterTag, &Weapon::HolsterAttachToTag },
{&EV_Weapon_HolsterOffset, &Weapon::SetHolsterOffset },
{&EV_Weapon_HolsterAngles, &Weapon::SetHolsterAngles },
{&EV_Weapon_HolsterScale, &Weapon::SetHolsterScale },
{&EV_Weapon_SetQuiet, &Weapon::SetQuiet },
{&EV_Weapon_SetLoopFire, &Weapon::SetLoopFire },
{&EV_Weapon_AutoPutaway, &Weapon::SetAutoPutaway },
{&EV_Weapon_UseNoAmmo, &Weapon::SetUseNoAmmo },
{&EV_Weapon_SetMeansOfDeath, &Weapon::SetMeansOfDeath },
{&EV_Weapon_SetWorldHitSpawn, &Weapon::SetWorldHitSpawn },
{&EV_Weapon_MakeNoise, &Weapon::MakeNoise },
{&EV_Weapon_SetType, &Weapon::SetWeaponType },
{&EV_Weapon_SetGroup, &Weapon::SetWeaponGroup },
{&EV_Weapon_SetZoom, &Weapon::SetZoom },
{&EV_Weapon_SetSemiAuto, &Weapon::SetSemiAuto },
{&EV_Weapon_AttachToHand, &Weapon::AttachToHand },
{&EV_Weapon_CantPartialReload, &Weapon::SetCantPartialReload },
{&EV_Weapon_FallingAngleAdjust, &Weapon::FallingAngleAdjust },
{&EV_Weapon_SetViewKick, &Weapon::SetViewKick },
{&EV_Weapon_DMSetFireDelay, &Weapon::SetDMFireDelay },
{&EV_Weapon_SetDMBulletDamage, &Weapon::SetDMBulletDamage },
{&EV_Weapon_SetDMBulletCount, &Weapon::SetDMBulletCount },
{&EV_Weapon_SetDMBulletSpread, &Weapon::SetDMBulletSpread },
{&EV_Weapon_SetZoomSpreadMult, &Weapon::SetZoomSpreadMult },
{&EV_Weapon_SetDMZoomSpreadMult, &Weapon::SetDMZoomSpreadMult },
{&EV_Weapon_SetFireSpreadMult, &Weapon::SetFireSpreadMult },
{&EV_Weapon_SetDMFireSpreadMult, &Weapon::SetDMFireSpreadMult },
{&EV_Weapon_DMAmmoRequired, &Weapon::SetDMAmmoRequired },
{&EV_Weapon_DMCantPartialReload, &Weapon::SetDMCantPartialReload },
{&EV_Weapon_DMStartAmmo, &Weapon::SetDMStartAmmo },
{&EV_Weapon_SetDMBulletRange, &Weapon::SetDMBulletRange },
{&EV_Weapon_DMCrosshair, &Weapon::DMCrosshair },
{&EV_Weapon_MovementSpeed, &Weapon::SetMovementSpeed },
{&EV_Weapon_DMMovementSpeed, &Weapon::SetDMMovementSpeed },
{&EV_Weapon_MaxFireMovement, &Weapon::SetMaxFireMovement },
{&EV_Weapon_ZoomMovement, &Weapon::SetZoomMovement },
{&EV_Weapon_AmmoPickupSound, &Weapon::EventAmmoPickupSound },
{&EV_Weapon_NoAmmoSound, &Weapon::EventNoAmmoSound },
{&EV_Weapon_MaxMovementSound, &Weapon::EventMaxMovementSound },
{&EV_Weapon_NumFireAnims, &Weapon::SetNumFireAnims },
{&EV_Weapon_NumFireAnims, &Weapon::SetWeaponSubtype },
{&EV_SetCookTime, &Weapon::SetCookTime },
{&EV_OverCooked, &Weapon::OnOverCooked },
{&EV_OverCooked_Warning, &Weapon::OnOverCookedWarning },
{&EV_Weapon_SetCurrentFireAnim, &Weapon::SetCurrentFireAnim },
{&EV_Weapon_SetSecondaryAmmoInHud, &Weapon::SetSecondaryAmmoInHud },
//
// Added in OPM
//
{&EV_Weapon_DoneAnimating, &Weapon::DoneAnimating },
{&EV_Weapon_GetPutaway, &Weapon::EventGetPutaway },
{NULL, NULL }
};
//======================
//Weapon::Weapon
//======================
Weapon::Weapon()
{
entflags |= ECF_WEAPON;
mAIRange = RANGE_SHORT;
if (LoadingSavegame) {
// Archive function will setup all necessary data
return;
}
// Set the weapon class to item by default
weapon_class = WEAPON_CLASS_ITEM;
order = 0;
// Owner of the weapon
owner = NULL;
// Fixed in 2.0
// Clear fire spread multiplier values
// Maximum spread multiplier while firing
m_fFireSpreadMultTimeCap[0] = 0;
m_fFireSpreadMultTimeCap[1] = 0;
// Starting rank of the weapon
rank = 0;
// Amount of ammo required for weapon
INITIALIZE_WEAPONMODE_VAR(ammorequired, 0);
// Starting ammo of the weapon
INITIALIZE_WEAPONMODE_VAR(startammo, 0);
// Amount of ammo the clip can hold
INITIALIZE_WEAPONMODE_VAR(ammo_clip_size, 0);
// Amount of ammo in clip
INITIALIZE_WEAPONMODE_VAR(ammo_in_clip, 0);
// Amount of time to pass before broadcasting a weapon sound again
nextweaponsoundtime = 0;
// Last fire state
m_fLastFireTime = 0;
m_eLastFireMode = (firemode_t)-11;
// The initial state of the weapon
weaponstate = WEAPON_HOLSTERED;
// Is the weapon droppable when the owner is killed
notdroppable = false;
// Aim animation for behavior of monsters
aimanim = -1;
aimframe = 0;
m_iAnimSlot = 0;
// start off unattached
attached = false;
// maximum effective firing distance (for autoaim)
maxrange = 1000;
// minimum safe firing distance (for AI)
minrange = 0;
// speed of the projectile (0 == infinite speed)
memset(projectilespeed, 0, sizeof(projectilespeed));
// Weapons don't move
setMoveType(MOVETYPE_NONE);
// What type of ammo this weapon fires
INITIALIZE_WEAPONMODE_VAR(firetype, FT_NONE);
INITIALIZE_WEAPONMODE_VAR(fire_delay, 0.1f);
// Init the bullet specs
INITIALIZE_WEAPONMODE_VAR(projectilespeed, 0);
INITIALIZE_WEAPONMODE_VAR(bulletdamage, 0);
INITIALIZE_WEAPONMODE_VAR(bulletlarge, 0);
INITIALIZE_WEAPONMODE_VAR(bulletcount, 1);
INITIALIZE_WEAPONMODE_VAR(bulletrange, 4096);
INITIALIZE_WEAPONMODE_VAR(bulletknockback, 0);
INITIALIZE_WEAPONMODE_VAR(bulletthroughwood, 0);
INITIALIZE_WEAPONMODE_VAR(bulletthroughmetal, 0);
INITIALIZE_WEAPONMODE_VAR(ammo_type, "");
INITIALIZE_WEAPONMODE_VAR(loopfire, false);
INITIALIZE_WEAPONMODE_VAR(quiet, qfalse);
INITIALIZE_WEAPONMODE_VAR(loopfire, qfalse);
INITIALIZE_WEAPONMODE_VAR(tracercount, 0);
INITIALIZE_WEAPONMODE_VAR(tracerfrequency, 0);
INITIALIZE_WEAPONMODE_VAR(tracerspeed, 1);
for (int i = 0; i < MAX_FIREMODES; i++) {
INITIALIZE_WEAPONMODE_VAR(viewkickmin[i], 0);
INITIALIZE_WEAPONMODE_VAR(viewkickmax[i], 0);
}
INITIALIZE_WEAPONMODE_VAR(m_fFireSpreadMultAmount, 0);
INITIALIZE_WEAPONMODE_VAR(m_fFireSpreadMultFalloff, 0);
INITIALIZE_WEAPONMODE_VAR(m_fFireSpreadMultCap, 0);
INITIALIZE_WEAPONMODE_VAR(m_fFireSpreadMultTime, 0);
INITIALIZE_WEAPONMODE_VAR(m_fFireSpreadMult, 0);
// Init the max amount of time a weapon may be charged (5 seconds)
INITIALIZE_WEAPONMODE_VAR(min_charge_time, 0);
INITIALIZE_WEAPONMODE_VAR(max_charge_time, 5);
charge_fraction = 1.0f;
// Tag to attach this weapon to on its owner when used in the left hand and in the right hand
attachToTag_offhand = "tag_weapon_left";
attachToTag_main = "tag_weapon_right";
// putaway is flagged true when the weapon should be put away by state machine
putaway = false;
// This is used for setting alternate fire functionality when initializing stuff
firemodeindex = FIRE_PRIMARY;
// Name and index
setName("Unnamed Weapon");
m_bCanPartialReload = qtrue;
m_bShareClip = qfalse;
// do better lighting on all weapons
edict->s.renderfx |= RF_EXTRALIGHT;
// Weapons do not auto aim automatically
autoaim = false;
// No crosshair visible
crosshair = false;
m_iZoom = 0;
m_bAutoZoom = false;
m_fZoomSpreadMult = 1.0f;
m_bSemiAuto = false;
// Weapons default to making noise
next_noise_time = 0;
next_noammo_time = 0;
next_maxmovement_time = 0;
// Obviously mustn't reload at first
m_bShouldReload = false;
// Used to keep track of last angles and scale before holstering
lastValid = qfalse;
lastScale = 1.0f;
holsterScale = 1.0f;
// Weapon will not be putaway by default when out of ammo
auto_putaway = qfalse;
// Weapon will be able to be used when it has no ammo
use_no_ammo = qtrue;
INITIALIZE_WEAPONMODE_VAR(meansofdeath, MOD_NONE);
// Set the stats
m_iNumShotsFired = 0;
m_iNumHits = 0;
m_iNumGroinShots = 0;
m_iNumHeadShots = 0;
m_iNumLeftArmShots = 0;
m_iNumRightArmShots = 0;
m_iNumLeftLegShots = 0;
m_iNumRightLegShots = 0;
m_iNumTorsoShots = 0;
// Set the default weapon group
m_csWeaponGroup = STRING_EMPTY;
m_fMovementSpeed = 1.0f;
m_fMaxFireMovement = 1.0f;
m_fZoomMovement = 1.0f;
m_sAmmoPickupSound = "snd_pickup_";
m_NoAmmoSound = "snd_noammo";
m_sMaxMovementSound = "snd_maxmovement";
// Always has a fire animation
m_iNumFireAnims = 1;
m_iCurrentFireAnim = 0;
// Default tag to use for muzzle and special effects
m_sTagBarrel = "tag_barrel";
m_iWeaponSubtype = 0;
// Not cooking by default
m_fCookTime = 0;
m_eCookModeIndex = FIRE_PRIMARY;
// Defaults to no secondary HUD
m_bSecondaryAmmoInHud = false;
PostEvent(EV_Weapon_IdleInit, 0);
last_owner_trigger_time = 0;
}
//======================
//Weapon::Weapon
//======================
Weapon::Weapon(const char *file)
{
// The tik file holds all the information available for a weapon
Weapon();
}
//======================
//Weapon::~Weapon
//======================
Weapon::~Weapon()
{
DetachGun();
if (owner) {
RemoveFromOwner();
}
entflags &= ~ECF_WEAPON;
}
//======================
//Weapon::Delete
//======================
void Weapon::Delete(void)
{
if (g_iInThinks) {
DetachGun();
}
Item::Delete();
}
//======================
//Weapon::SetRankEvent
//======================
void Weapon::SetRankEvent(Event *ev)
{
SetRank(ev->GetInteger(1), ev->GetInteger(2));
}
//======================
//Weapon::SetWeaponType
//======================
void Weapon::SetWeaponType(Event *ev)
{
weapon_class = G_WeaponClassNameToNum(ev->GetString(1));
}
//======================
//Weapon::SetWeaponGroup
//======================
void Weapon::SetWeaponGroup(Event *ev)
{
m_csWeaponGroup = ev->GetConstString(1);
}
//======================
//Weapon::SetZoom
//======================
void Weapon::SetZoom(Event *ev)
{
m_iZoom = ev->GetInteger(1);
if (ev->NumArgs() > 1) {
int autozoom = ev->GetInteger(2);
if (autozoom) {
m_bAutoZoom = qtrue;
}
}
}
//======================
//Weapon::SetSemiAuto
//======================
void Weapon::SetSemiAuto(Event *ev)
{
m_bSemiAuto = qtrue;
}
//======================
//Weapon::SetAutoPutaway
//======================
void Weapon::SetAutoPutaway(Event *ev)
{
auto_putaway = ev->GetBoolean(1);
}
//======================
//Weapon::SetUseNoAmmo
//======================
void Weapon::SetUseNoAmmo(Event *ev)
{
use_no_ammo = ev->GetBoolean(1);
}
//======================
//Weapon::SetStartAmmo
//======================
void Weapon::SetStartAmmo(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
startammo[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::SetDMStartAmmo
//======================
void Weapon::SetDMStartAmmo(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
startammo[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::SetMaxChargeTime
//======================
void Weapon::SetMaxChargeTime(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
max_charge_time[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetMaxChargeTime
//======================
void Weapon::SetMinChargeTime(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
min_charge_time[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::GetMinChargeTime
//======================
float Weapon::GetMinChargeTime(firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
return min_charge_time[mode];
}
//======================
//Weapon::GetMinChargeTime
//======================
float Weapon::GetMaxChargeTime(firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
return max_charge_time[mode];
}
//======================
//Weapon::SetAmmoRequired
//======================
void Weapon::SetAmmoRequired(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
ammorequired[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::SetDMAmmoRequired
//======================
void Weapon::SetDMAmmoRequired(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
ammorequired[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::GetAmmoType
//======================
str Weapon::GetAmmoType(firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
mode = m_bShareClip ? FIRE_PRIMARY : mode;
return ammo_type[mode];
}
//======================
//Weapon::SetAmmoType
//======================
void Weapon::SetAmmoType(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
if ((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES)) {
ammo_type[firemodeindex] = ev->GetString(1);
} else {
warning("Weapon::SetAmmoType", "Invalid mode %d\n", firemodeindex);
return;
}
}
//======================
//Weapon::SetAmmoAmount
//======================
void Weapon::SetAmmoAmount(int amount, firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
mode = m_bShareClip ? FIRE_PRIMARY : mode;
// If the clip can hold ammo, then set the amount in the clip to the specified amount
if (ammo_clip_size[mode]) {
ammo_in_clip[mode] = amount;
}
}
//======================
//Weapon::GetClipSize
//======================
int Weapon::GetClipSize(firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if ((mode >= 0) && (mode < MAX_FIREMODES)) {
return ammo_clip_size[mode];
} else {
warning("Weapon::GetClipSize", "Invalid mode %d\n", mode);
return 0;
}
}
//======================
//Weapon::UseAmmo
//======================
void Weapon::UseAmmo(int amount, firemode_t mode)
{
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if (UnlimitedAmmo(mode) && (!owner || !owner->isClient())) {
return;
}
// Remove ammo from the clip if it's available
if (ammo_clip_size[mode]) {
ammo_in_clip[mode] -= amount;
if (ammo_in_clip[mode] < 0) {
warning("UseAmmo", "Used more ammo than in clip.\n");
ammo_in_clip[mode] = 0;
}
if (!ammo_in_clip[mode]) {
SetShouldReload(qtrue);
}
owner->AmmoAmountInClipChanged(ammo_type[mode], ammo_in_clip[mode]);
} else {
assert(owner);
if (owner && owner->isClient() && !UnlimitedAmmo(mode)) {
// Remove ammo from the player's inventory
owner->UseAmmo(ammo_type[mode], ammorequired[mode]);
}
}
}
/*
//======================
//Weapon::GetMuzzlePosition
//======================
void Weapon::GetMuzzlePosition
(
Vector *position,
Vector *forward,
Vector *right,
Vector *up
)
{
orientation_t weap_or, barrel_or, orient;
Vector pos;
vec3_t mat[3]={0,0,0,0,0,0,0,0,0};
vec3_t orient[3];
int i, mi, tagnum;
assert( owner );
// We should always have an owner
if ( !owner )
{
return;
}
// Get the owner's weapon orientation ( this is custom code and doesn't use the GetTag function
// because we need to use the saved off fire_frame and fire_animation indexes from the owner
mi = owner->edict->s.modelindex;
tagnum = gi.Tag_NumForName( mi, current_attachToTag.c_str() );
if ( tagnum < 0 )
{
warning( "Weapon::GetMuzzlePosition", "Could not find tag \"%s\"", current_attachToTag.c_str() );
pos = owner->centroid;
AnglesToAxis( owner->angles, mat );
goto out;
}
// Get the orientation based on the frame and anim stored off in the owner.
// This is to prevent weird timing with getting orientations on different frames of firing
// animations and the orientations will not be consistent.
AnglesToAxis( owner->angles, owner->orientation );
orient = gi.Tag_OrientationEx( mi,
owner->CurrentAnim( legs ),
owner->CurrentFrame( legs ),
tagnum & TAG_MASK,
owner->edict->s.scale,
owner->edict->s.bone_tag,
owner->edict->s.bone_quat,
0,
0,
1.0f,
( owner->edict->s.anim & ANIM_BLEND ) != 0,
( owner->edict->s.torso_anim & ANIM_BLEND ) != 0,
owner->GetFiringAnim(),
owner->GetFiringFrame(),
0,
0,
1.0f
);
// Transform the weapon's orientation through the owner's orientation
// Player orientation is normally based on the player's view, but we need
// it to be based on the model's orientation, so we calculate it here.
AnglesToAxis( owner->angles, orient );
VectorCopy( owner->origin, weap_or.origin );
for ( i=0; i<3; i++ )
{
VectorMA( weap_or.origin, orient.origin[i], orient[i], weap_or.origin );
}
MatrixMultiply( orient.axis, orient, weap_or.axis );
// For debugging
G_DrawCoordSystem( weap_or.origin, weap_or.axis[0], weap_or.axis[1], weap_or.axis[2], 50 );
// Get the tag_barrel orientation from the weapon
if ( !this->GetRawTag( "tag_barrel", &barrel_or ) )
{
//warning( "Weapon::GetMuzzlePosition", "Could not find tag_barrel\n" );
pos = owner->centroid;
AnglesToAxis( owner->angles, mat );
goto out;
}
//gi.DPrintf( "anim:%d frame:%d\n", this->CurrentAnim(), this->CurrentFrame() );
// Translate the barrel's orientation through the weapon's orientation
VectorCopy( weap_or.origin, pos );
for ( i = 0 ; i < 3 ; i++ )
{
VectorMA( pos, barrel_or.origin[i], weap_or.axis[i], pos );
}
MatrixMultiply( barrel_or.axis, weap_or.axis, mat );
#if 0
gi.DPrintf( "own_angles: %0.2f %0.2f %0.2f\n", owner->angles[0], owner->angles[1], owner->angles[2] );
gi.DPrintf( "own_orient: %0.2f %0.2f %0.2f\n", owner->orientation[0][0], owner->orientation[0][1], owner->orientation[0][2] );
gi.DPrintf( "bone forward: %0.2f %0.2f %0.2f\n", orient.axis[0][0], orient.axis[0][1], orient.axis[0][2] );
gi.DPrintf( "barrel forward: %0.2f %0.2f %0.2f\n", barrel_or.axis[0][0], barrel_or.axis[0][1], barrel_or.axis[0][2] );
gi.DPrintf( "weapon forward: %0.2f %0.2f %0.2f\n", weap_or.axis[0][0], weap_or.axis[0][1], weap_or.axis[0][2] );
gi.DPrintf( "mat forward: %0.2f %0.2f %0.2f\n \n", mat[0][0], mat[0][1], mat[0][2] );
#endif
// For debugging
G_DrawCoordSystem( pos, mat[0], mat[1], mat[2], 30 );
// Ok - we now have a position, forward, right, and up
out:
if ( position )
{
*position = pos;
}
if ( forward )
{
*forward = mat[0];
}
if ( right )
{
*right = mat[1];
}
if ( up )
{
*up = mat[2];
}
}
*/
//======================
//Weapon::GetMuzzlePosition
//======================
void Weapon::GetMuzzlePosition(vec3_t position, vec3_t vBarrelPos, vec3_t forward, vec3_t right, vec3_t up)
{
orientation_t weap_or, barrel_or, orient;
Vector delta;
Vector aim_angles;
int tagnum;
int i;
owner = (Player *)this->owner.Pointer();
// We should always have an owner
if (!owner) {
if (IsSubclassOfVehicleTurretGun()) {
VehicleTurretGun *t = (VehicleTurretGun *)this;
owner = (Player *)t->GetRemoteOwner().Pointer();
}
}
if (owner) {
tagnum = gi.Tag_NumForName(owner->edict->tiki, current_attachToTag.c_str());
// Get the orientation based on the frame and anim stored off in the owner.
// This is to prevent weird timing with getting orientations on different frames of firing
// animations and the orientations will not be consistent.
AnglesToAxis(owner->angles, owner->orientation);
orient = G_TIKI_Orientation(owner->edict, tagnum);
if (owner->IsSubclassOfPlayer()) {
Player *player;
// Use the player's torso angles to determine direction, but use the actual barrel to determine position
player = static_cast<Player *>(owner.Pointer());
if (forward || right || up) {
Vector aimAng = player->m_vViewAng;
vec3_t vrAim;
//
// Added in OPM
//
// In VR the shot follows the weapon hand, not the head. The
// camera stays where the player is looking - moving it to the
// controller would be unusable - so only the trace moves.
//
// Pitch comes straight from the controller; the head
// contributes nothing to it, and blending the two would fight
// the player's own wrist. Yaw is the view yaw plus how far the
// hand leads the head, because the controller's absolute yaw
// means nothing to the game: the player can turn on the spot
// with the stick, and the play space has an arbitrary forward.
//
// This is what RTCWQuest does, at rtcw/src/game/g_weapon.c:1904.
//
if (gi.VR_GetWeaponAim && gi.VR_GetWeaponAim(vrAim)) {
aimAng[PITCH] = vrAim[PITCH];
aimAng[YAW] = AngleNormalize360(aimAng[YAW] + vrAim[YAW]);
}
AngleVectors(aimAng, forward, right, up);
}
VectorCopy(player->m_vViewPos, position);
if (this->GetRawTag(GetTagBarrel(), &barrel_or) && vBarrelPos) {
VectorCopy(owner->origin, vBarrelPos);
for (i = 0; i < 3; i++) {
VectorMA(vBarrelPos, orient.origin[i], owner->orientation[i], vBarrelPos);
}
MatrixMultiply(orient.axis, owner->orientation, weap_or.axis);
for (i = 0; i < 3; i++) {
VectorMA(vBarrelPos, barrel_or.origin[i], weap_or.axis[i], vBarrelPos);
}
} else if (vBarrelPos) {
VectorCopy(position, vBarrelPos);
}
} else {
VectorCopy(owner->origin, position);
for (i = 0; i < 3; i++) {
VectorMA(position, orient.origin[i], owner->orientation[i], position);
}
MatrixMultiply(orient.axis, owner->orientation, weap_or.axis);
if (this->GetRawTag(GetTagBarrel(), &barrel_or)) {
for (i = 0; i < 3; i++) {
VectorMA(position, barrel_or.origin[i], weap_or.axis[i], position);
}
}
if (vBarrelPos) {
VectorCopy(position, vBarrelPos);
}
delta = owner->GunTarget(false, position, weap_or.axis[0]) - position;
aim_angles = delta.toAngles();
if (forward || right || up) {
AngleVectors(aim_angles, forward, right, up);
}
}
} else {
if (forward || right || up) {
AngleVectors(angles, forward, right, up);
}
VectorCopy(origin, position);
if (this->GetRawTag(GetTagBarrel(), &barrel_or)) {
AnglesToAxis(angles, weap_or.axis);
for (i = 0; i < 3; i++) {
VectorMA(position, barrel_or.origin[i], weap_or.axis[i], position);
}
}
if (vBarrelPos) {
VectorCopy(position, vBarrelPos);
}
}
}
//======================
//Weapon::SetAmmoClipSize
//======================
void Weapon::SetAmmoClipSize(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
ammo_clip_size[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::SetAmmoInClip
//======================
void Weapon::SetAmmoInClip(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
ammo_in_clip[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::SetShareClip
//======================
void Weapon::SetShareClip(Event *ev)
{
m_bShareClip = qtrue;
}
//======================
//Weapon::SetTagBarrel
//======================
void Weapon::SetTagBarrel(const char *tagBarrel)
{
m_sTagBarrel = tagBarrel;
}
//======================
//Weapon::GetTagBarrel
//======================
str Weapon::GetTagBarrel() const
{
return m_sTagBarrel;
}
//======================
//Weapon::Shoot
//======================
void Weapon::Shoot(Event *ev)
{
Vector pos, forward, right, up, vBarrel, delta;
firemode_t mode = FIRE_PRIMARY;
qboolean mc;
if (ev->NumArgs() > 0) {
mode = WeaponModeNameToNum(ev->GetString(1));
if (mode == FIRE_ERROR) {
return;
}
if (ev->NumArgs() > 1) {
SetTagBarrel(ev->GetString(2));
}
}
if (owner && owner->IsSubclassOfSentient() && owner->m_bOvercookDied) {
owner->m_bOvercookDied = false;
return;
}
mc = MuzzleClear();
// If we are in loopfire, we need to keep checking ammo and using it up
if (loopfire[mode]) {
if (HasAmmo(mode) && mc) {
// Use up the appropriate amount of ammo, it's already been checked that we have enough
UseAmmo(ammorequired[mode], mode);
} else {
ForceIdle();
}
}
// If the muzzle is not clear, then change to a clear animation, otherwise punt out.
if (!mc) {
ForceIdle();
return;
}
GetMuzzlePosition(pos, vBarrel, forward, right, up);
ApplyFireKickback(forward, 1000.0);
if (firetype[mode] != FT_LANDMINE || CanPlaceLandmine(pos, owner)) {
if (m_fFireSpreadMultAmount[mode] != 0.0f) {
float fTime = level.time - m_fFireSpreadMultTime[mode];
if (fTime <= m_fFireSpreadMultTimeCap[mode]) {
float fDecay = fTime * m_fFireSpreadMultFalloff[mode];
if (m_fFireSpreadMult[mode] <= 0.0f) {
m_fFireSpreadMult[mode] -= fDecay;
if (m_fFireSpreadMult[mode] > 0.0f) {
m_fFireSpreadMult[mode] = 0.0f;
}
} else {
m_fFireSpreadMult[mode] -= fDecay;
if (m_fFireSpreadMult[mode] < 0.0f) {
m_fFireSpreadMult[mode] = 0.0f;
}
}
} else {
m_fFireSpreadMult[mode] = 0.0f;
}
m_fFireSpreadMultTime[mode] = level.time;
}
switch (firetype[mode]) {
case FT_PROJECTILE:
ProjectileAttack(pos, forward, owner, projectileModel[mode], charge_fraction, 0, this);
break;
case FT_LANDMINE:
PlaceLandmine(pos, owner, projectileModel[mode], this);
break;
case FT_DEFUSE:
DefuseObject(forward, owner, bulletrange[mode]);
break;
case FT_BULLET:
{
Vector vSpread;
float fSpreadFactor;
int tracerFrequency;
SafePtr<Sentient> ownerPtr;
if (owner) {
if (owner->client) {
Player *player = (Player *)owner.Pointer();
fSpreadFactor = GetSpreadFactor(mode);
vSpread = bulletspreadmax[mode] * fSpreadFactor;
fSpreadFactor = 1.0f - fSpreadFactor;
vSpread += bulletspread[mode] * fSpreadFactor;
vSpread *= m_fFireSpreadMult[mode] + 1.0f;
if (m_iZoom) {
if (player->IsSubclassOfPlayer() && player->IsZoomed()) {
vSpread *= 1.0f + fSpreadFactor * (m_fZoomSpreadMult - 1.0f);
}
}
}
} else {
vSpread = (bulletspreadmax[mode] + bulletspread[mode]) * 0.5f;
}
// Don't display tracers if the owner is a player for non-turret weapons
// It would have some artifacts for the first person model, as the tracer is created from the world model
if ((g_target_game == TG_MOH || g_gametype->integer == GT_SINGLE_PLAYER) && owner
&& owner->IsSubclassOfPlayer()) {
if (IsSubclassOfTurretGun()) {
tracerFrequency = 3;
} else {
tracerFrequency = 0;
}
} else {
tracerFrequency = tracerfrequency[mode];
}
ownerPtr = owner;
if (!owner && IsSubclassOfVehicleTurretGun()) {
VehicleTurretGun *turretGun = static_cast<VehicleTurretGun *>(this);
ownerPtr = turretGun->GetRemoteOwner();
}
BulletAttack(
pos,
vBarrel,
forward,
right,
up,
bulletrange[mode],
bulletdamage[mode],
bulletlarge[mode],
bulletknockback[mode],
0,
GetMeansOfDeath(mode),
vSpread,
bulletcount[mode],
ownerPtr,
tracerFrequency,
&tracercount[mode],
bulletthroughwood[mode],
bulletthroughmetal[mode],
this,
tracerspeed[mode]
);
}
break;
case FT_FAKEBULLET:
{
Vector vSpread;
float fSpreadFactor;
if (owner) {
if (owner->client) {
Player *player = (Player *)owner.Pointer();
fSpreadFactor = player->velocity.length() / sv_runspeed->integer;
if (fSpreadFactor > 1.0f) {
fSpreadFactor = 1.0f;
}
vSpread = bulletspreadmax[mode] * fSpreadFactor;
fSpreadFactor = 1.0f - fSpreadFactor;
vSpread += bulletspread[mode] * fSpreadFactor;
vSpread *= m_fFireSpreadMult[mode] + 1.0f;
if (m_iZoom) {
if (player->IsSubclassOfPlayer() && player->IsZoomed()) {
vSpread *= 1.0f + fSpreadFactor * (m_fZoomSpreadMult - 1.0f);
}
}
}
} else {
vSpread = (bulletspreadmax[mode] + bulletspread[mode]) * 0.5f;
}
FakeBulletAttack(
pos,
vBarrel,
forward,
right,
up,
bulletrange[mode],
bulletdamage[mode],
bulletlarge[mode],
vSpread,
bulletcount[mode],
owner,
tracerfrequency[mode],
&tracercount[mode],
tracerspeed[mode]
);
}
break;
case FT_SPECIAL_PROJECTILE:
SpecialFireProjectile(pos, forward, right, up, owner, projectileModel[mode], charge_fraction);
break;
case FT_CLICKITEM:
ClickItemAttack(pos, forward, bulletrange[mode], owner);
break;
case FT_HEAVY:
if (owner || !IsSubclassOfVehicleTurretGun()) {
HeavyAttack(pos, forward, projectileModel[mode], 0, owner, this);
} else {
VehicleTurretGun *turret = (VehicleTurretGun *)this;
if (turret->UseRemoteControl() && turret->GetRemoteOwner()) {
HeavyAttack(pos, forward, projectileModel[mode], 0, turret->GetRemoteOwner(), this);
} else {
HeavyAttack(pos, forward, projectileModel[mode], 0, this, this);
}
}
break;
case FT_MELEE:
{
Vector melee_pos, melee_end;
Vector dir;
float damage;
meansOfDeath_t meansofdeath;
float knockback;
if (owner) {
dir = owner->centroid - pos;
} else {
dir = centroid - pos;
}
dir.z = 0;
melee_pos = pos - forward * dir.length();
melee_end = melee_pos + forward * bulletrange[mode];
damage = bulletdamage[mode];
knockback = 0;
meansofdeath = GetMeansOfDeath(mode);
Container<Entity *> victimlist;
m_iNumShotsFired++;
if (MeleeAttack(
melee_pos, melee_end, damage, owner, meansofdeath, 8, -8, 8, knockback, true, &victimlist
)) {
m_iNumHits++;
m_iNumTorsoShots++;
}
}
break;
default:
// none or unsupported
break;
}
if (!quiet[mode] && next_noise_time <= level.time) {
if (g_gametype->integer == GT_SINGLE_PLAYER) {
BroadcastAIEvent(AI_EVENT_WEAPON_FIRE, 1500);
} else {
//
// Added in OPM
// Weapon firing sounds can be heard up to 8000 units away
// (from ubersound.scr)
BroadcastAIEvent(AI_EVENT_WEAPON_FIRE, Q_clamp_float(world->GetRadius() * 0.5, 1500, 8000));
}
next_noise_time = level.time + 1;
}
if (owner && owner->client) {
Vector vAngles = owner->GetViewAngles();
if (viewkickmin[mode][0] != 0.0f || viewkickmax[mode][0] != 0.0f) {
vAngles[0] += random() * (viewkickmax[mode][0] - viewkickmin[mode][0]) + viewkickmin[mode][0];
}
if (viewkickmin[1][0] != 0.0f || viewkickmax[1][0] != 0.0f) {
vAngles[1] += random() * (viewkickmax[mode][1] - viewkickmin[mode][1]) + viewkickmin[mode][1];
}
owner->SetViewAngles(vAngles);
}
if (m_fFireSpreadMultAmount[mode]) {
m_fFireSpreadMult[mode] += m_fFireSpreadMultAmount[mode];
if (m_fFireSpreadMultCap[mode] > 0) {
if (m_fFireSpreadMult[mode] > m_fFireSpreadMultCap[mode]) {
m_fFireSpreadMult[mode] = m_fFireSpreadMultCap[mode];
} else if (m_fFireSpreadMult[mode] < 0) {
m_fFireSpreadMult[mode] = 0;
}
} else if (m_fFireSpreadMultCap[mode] < 0) {
if (m_fFireSpreadMult[mode] < m_fFireSpreadMultCap[mode]) {
m_fFireSpreadMult[mode] = m_fFireSpreadMultCap[mode];
} else if (m_fFireSpreadMult[mode] > 0) {
m_fFireSpreadMult[mode] = 0;
}
}
}
m_fLastFireTime = level.time;
m_eLastFireMode = mode;
} else {
// Landmine weapon
if (ammo_clip_size[mode]) {
UseAmmo(-ammorequired[mode], mode);
} else if (owner && owner->isClient() && !UnlimitedAmmo(mode)) {
owner->UseAmmo(ammo_type[mode], -ammorequired[mode]);
}
}
}
//======================
//Weapon::ApplyFireKickback
//======================
void Weapon::ApplyFireKickback(const Vector& org, float kickback) {}
//======================
//Weapon::SetAimAnim
//======================
void Weapon::SetAimAnim(Event *ev)
{
str anim;
anim = ev->GetString(1);
aimanim = gi.Anim_NumForName(edict->tiki, anim.c_str());
aimframe = ev->GetInteger(2);
}
//======================
//Weapon::SetOwner
//======================
void Weapon::SetOwner(Sentient *ent)
{
assert(ent);
if (!ent) {
// return to avoid any buggy behaviour
return;
}
Item::SetOwner(ent);
setOrigin(vec_zero);
setAngles(vec_zero);
}
//======================
//Weapon::AmmoAvailable
//======================
int Weapon::AmmoAvailable(firemode_t mode)
{
// Returns the amount of ammo the owner has that is available for use
assert((mode >= 0) && (mode < MAX_FIREMODES));
mode = m_bShareClip ? FIRE_PRIMARY : mode;
// Make sure there is an owner before querying the amount of ammo
if (owner) {
return owner->AmmoCount(ammo_type[mode]);
} else {
if (ammo_clip_size[mode]) {
return ammo_clip_size[mode];
} else {
return ammorequired[mode];
}
}
}
//======================
//Weapon::UnlimitedAmmo
//======================
qboolean Weapon::UnlimitedAmmo(firemode_t mode)
{
if (!owner) {
return true;
}
if (!owner->isClient() || DM_FLAG(DF_INFINITE_AMMO)) {
return true;
} else if (!Q_stricmp(ammo_type[mode], "None")) {
return true;
}
return false;
}
//======================
//Weapon::HasAmmo
//======================
qboolean Weapon::HasAmmo(firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
if (!((mode >= 0) && (mode < MAX_FIREMODES))) {
warning("Weapon::HasAmmo", "Invalid mode %d\n", mode);
return false;
}
if (m_bShareClip) {
// share the clip
mode = FIRE_PRIMARY;
}
if (UnlimitedAmmo(mode)) {
return true;
}
// If the weapon uses a clip, check for ammo in the right clip
if (ammo_clip_size[mode] && HasAmmoInClip(mode)) {
return true;
} else // Otherwise check if ammo is available in general
{
if (!ammorequired[mode]) {
return true;
}
return (AmmoAvailable(mode) >= ammorequired[mode]);
}
}
//======================
//Weapon::HasAmmoInClip
//======================
qboolean Weapon::HasAmmoInClip(firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
if (!((mode >= 0) && (mode < MAX_FIREMODES))) {
warning("Weapon::HasAmmoInClip", "Invalid mode %d\n", mode);
}
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if (ammo_clip_size[mode]) {
if (ammo_in_clip[mode] >= ammorequired[mode]) {
return true;
}
} else {
if (!ammorequired[mode]) {
return true;
}
return (AmmoAvailable(mode) >= ammorequired[mode]);
}
return false;
}
//======================
//Weapon::ReadyToFire
//======================
qboolean Weapon::ReadyToFire(firemode_t mode, qboolean playsound)
{
if (owner && owner->IsSubclassOfSentient()) {
// Clear the cook flag
owner->m_bOvercookDied = false;
}
if (!level.playerfrozen && m_iZoom && mode == FIRE_SECONDARY) {
return qtrue;
}
// Make sure the weapon is in the ready state and the weapon has ammo
if (m_eLastFireMode != mode || level.time > (m_fLastFireTime + FireDelay(mode))) {
if (HasAmmoInClip(mode)) {
if (m_fMaxFireMovement >= 1.f) {
return qtrue;
}
if (!owner) {
return qtrue;
}
if ((owner->velocity.lengthXY() / sv_runspeed->value) <= (m_fMovementSpeed * m_fMaxFireMovement)) {
return qtrue;
}
if (playsound) {
if (level.time > next_maxmovement_time) {
Sound(m_sMaxMovementSound);
}
next_maxmovement_time = level.time + level.frametime + 0.5;
}
} else if (playsound) {
if (level.time > next_noammo_time) {
Sound(m_NoAmmoSound);
}
next_noammo_time = level.time + level.frametime + FireDelay(mode);
}
}
return qfalse;
}
//======================
//Weapon::PutAway
//======================
void Weapon::PutAway(void)
{
// set the putaway flag to true, so the state machine know to put this weapon away
putaway = true;
}
//======================
//Weapon::DetachFromOwner
//======================
void Weapon::DetachFromOwner(void)
{
DetachGun();
weaponstate = WEAPON_HOLSTERED;
}
//======================
//Weapon::AttachToOwner
//======================
void Weapon::AttachToOwner(weaponhand_t hand)
{
AttachGun(hand);
ForceIdle();
}
//======================
//Weapon::AttachToHolster
//======================
void Weapon::AttachToHolster(weaponhand_t hand)
{
AttachGun(hand, qtrue);
SetWeaponAnim("holster", EV_Weapon_Idle);
}
//======================
//Weapon::Drop
//======================
qboolean Weapon::Drop(void)
{
Vector temp;
if (!owner) {
return false;
}
if (!IsDroppable()) {
return false;
}
if (attached) {
Vector vAng;
orientation_t oTag;
Entity *pParent;
if (edict->s.parent == ENTITYNUM_NONE || edict->s.tag_num < 0) {
pParent = owner;
vAng = pParent->angles;
} else {
vAng = vec_zero;
pParent = G_GetEntity(edict->s.parent);
AnglesToAxis(pParent->angles, pParent->orientation);
pParent->GetTag(edict->s.tag_num, &oTag);
MatrixToEulerAngles(oTag.axis, vAng);
}
setAngles(vAng);
DetachGun();
} else {
temp[2] = 40;
setOrigin(owner->origin + temp);
setAngles(owner->angles);
}
setSize(Vector(-12, -12, -2), Vector(12, 12, 12));
// stop animating
StopWeaponAnim();
// drop the weapon
PlaceItem();
temp = centroid - owner->centroid;
temp[2] = 0;
VectorNormalize(temp);
temp *= 75.0f;
temp[0] += G_CRandom(25);
temp[1] += G_CRandom(25);
temp[2] = G_CRandom(50) + 150;
velocity = owner->velocity * 0.5 + temp;
avelocity = Vector(0, G_CRandom(120), 0);
spawnflags |= DROPPED_PLAYER_ITEM;
if (owner && owner->isClient()) {
if (owner->deadflag && g_dropclips->integer > 0) {
int ammo;
if (ammo_clip_size[FIRE_PRIMARY]) {
// Fixed in OPM
// Always drop at least 1 because of grenades
startammo[FIRE_PRIMARY] = g_dropclips->integer * Q_max(ammo_in_clip[FIRE_PRIMARY], 1);
} else {
startammo[FIRE_PRIMARY] = g_dropclips->integer;
}
// Fixed in OPM
// Use the max between the available ammo and the ammo in clip
ammo = Q_max(AmmoAvailable(FIRE_PRIMARY), ammo_in_clip[FIRE_PRIMARY]);
if (startammo[FIRE_PRIMARY] > ammo) {
startammo[FIRE_PRIMARY] = ammo;
}
if (ammo_clip_size[FIRE_SECONDARY]) {
startammo[FIRE_SECONDARY] = g_dropclips->integer * Q_max(ammo_in_clip[FIRE_SECONDARY], 1);
} else {
startammo[FIRE_SECONDARY] = g_dropclips->integer;
}
ammo = Q_max(AmmoAvailable(FIRE_SECONDARY), ammo_in_clip[FIRE_SECONDARY]);
if (startammo[FIRE_SECONDARY] > ammo) {
startammo[FIRE_SECONDARY] = ammo;
}
ammo_in_clip[FIRE_PRIMARY] = 0;
ammo_in_clip[FIRE_SECONDARY] = 0;
} else {
startammo[FIRE_PRIMARY] = AmmoAvailable(FIRE_PRIMARY);
owner->takeAmmoType(ammo_type[FIRE_PRIMARY]);
}
} else {
if (ammo_clip_size[FIRE_PRIMARY] && ammo_in_clip[FIRE_PRIMARY]) {
startammo[FIRE_PRIMARY] = ammo_in_clip[FIRE_PRIMARY];
} else {
startammo[FIRE_PRIMARY] >>= 2;
}
if (ammo_clip_size[FIRE_SECONDARY] && ammo_in_clip[FIRE_SECONDARY]) {
startammo[FIRE_SECONDARY] = ammo_in_clip[FIRE_SECONDARY];
} else {
startammo[FIRE_SECONDARY] >>= 2;
}
if (startammo[FIRE_PRIMARY] == 0) {
startammo[FIRE_PRIMARY] = 1;
}
if (startammo[FIRE_SECONDARY] == 0) {
startammo[FIRE_SECONDARY] = 1;
}
}
// Wait some time before the last owner can pickup this weapon
last_owner = owner;
last_owner_trigger_time = level.time + 2.0f;
// Cancel reloading events
CancelEventsOfType(EV_Weapon_DoneReloading);
// Remove this from the owner's item list
RemoveFromOwner();
if (g_droppeditemlife->value > 0) {
PostEvent(EV_Remove, g_droppeditemlife->value);
}
PostEvent(EV_Weapon_FallingAngleAdjust, level.frametime);
return true;
}
//======================
//Weapon::Charge
//======================
void Weapon::Charge(firemode_t mode)
{
if (mode == FIRE_PRIMARY) {
if (m_fCookTime > 0) {
m_eCookModeIndex = mode;
//
// Post cook event
//
PostEvent(EV_OverCooked, m_fCookTime);
PostEvent(EV_OverCooked_Warning, m_fCookTime - 1);
}
SetWeaponAnim("charge");
} else if (mode == FIRE_SECONDARY) {
if (m_fCookTime > 0) {
m_eCookModeIndex = mode;
//
// Post cook event
//
PostEvent(EV_OverCooked, m_fCookTime);
PostEvent(EV_OverCooked_Warning, m_fCookTime - 1);
}
SetWeaponAnim("secondarycharge");
}
}
//======================
//Weapon::OnOverCookedWarning
//======================
void Weapon::OnOverCookedWarning(Event *ev) {}
//======================
//Weapon::OnOverCooked
//======================
void Weapon::OnOverCooked(Event *ev)
{
if (!owner) {
return;
}
if (projectileModel[m_eCookModeIndex].length()) {
Entity *spawnedEnt;
SpawnArgs sp;
sp.setArg("model", projectileModel[m_eCookModeIndex]);
spawnedEnt = static_cast<Entity *>(sp.Spawn());
if (spawnedEnt && spawnedEnt->IsSubclassOfProjectile()) {
Event *newev;
Projectile *proj = static_cast<Projectile *>(spawnedEnt);
trace_t trace;
proj->origin = owner->origin;
proj->angles = owner->angles;
proj->owner = owner->entnum;
proj->edict->r.ownerNum = owner->entnum;
trace = G_Trace(
proj->origin,
vec_zero,
vec_zero,
owner->origin,
static_cast<Entity *>(owner.Pointer()),
owner->edict->clipmask,
qfalse,
"Weapon::OnOverCooked"
);
if (trace.ent && trace.ent->entity->entnum == world->entnum) {
proj->origin = owner->origin;
}
newev = new Event(EV_Projectile_Explode);
newev->AddEntity(NULL);
if (g_gametype->integer == GT_SINGLE_PLAYER) {
newev->AddFloat(1000.0);
}
if (owner->IsSubclassOfSentient()) {
owner->m_bOvercookDied = true;
}
proj->ProcessEvent(newev);
}
}
}
//======================
//Weapon::ReleaseFire
//======================
void Weapon::ReleaseFire(firemode_t mode, float charge_time)
{
// Make sure to stop the wepaon from cooking
CancelEventsOfType(EV_OverCooked);
CancelEventsOfType(EV_OverCooked_Warning);
// Calculate and store off the charge fraction to use when the weapon actually shoots
// Clamp to max_charge_time
if (charge_time - min_charge_time[mode] >= 0.0f) {
if (charge_time <= max_charge_time[mode]) {
charge_fraction = charge_time / max_charge_time[mode];
} else {
charge_fraction = 1.0f;
}
} else {
charge_fraction = 0.0f;
}
// Call regular fire function
Fire(mode);
}
//======================
//Weapon::GetFireAnim
//======================
const char *Weapon::GetFireAnim() const
{
if (m_iNumFireAnims > 1) {
static char tagname[256];
Com_sprintf(tagname, sizeof(tagname), "fire_%d", m_iCurrentFireAnim + 1);
return tagname;
} else {
return "fire";
}
}
//======================
//Weapon::Fire
//======================
void Weapon::Fire(firemode_t mode)
{
Event *done_event = NULL;
Vector pos;
// Sanity check the mode
assert((mode >= 0) && (mode < MAX_FIREMODES));
// If we are in loopfire mode, then we don't pass a DoneFiring event
if (!loopfire[mode]) {
// The DoneFiring event requires to know the firing mode so save that off in the event
done_event = new Event(EV_Weapon_DoneFiring);
done_event->AddInteger(mode);
}
if (!MuzzleClear()) {
SetWeaponAnim("clear", done_event);
weaponstate = WEAPON_READY;
return;
}
// Use up the appropriate amount of ammo, it's already been checked that we have enough
UseAmmo(ammorequired[mode], mode);
// Set the state of the weapon to FIRING
weaponstate = WEAPON_FIRING;
// Cancel any old done firing events
CancelEventsOfType(EV_Weapon_DoneFiring);
// Play the correct animation
if (mode == FIRE_PRIMARY) {
if (m_iNumFireAnims > 1) {
m_iCurrentFireAnim++;
if (m_iCurrentFireAnim >= m_iNumFireAnims) {
m_iCurrentFireAnim = 0;
}
}
if (ammo_clip_size[FIRE_PRIMARY] && !ammo_in_clip[FIRE_PRIMARY] && HasAnim("fire_empty")) {
SetWeaponAnim("fire_empty", done_event);
} else {
SetWeaponAnim(GetFireAnim(), done_event);
}
} else if (mode == FIRE_SECONDARY) {
if (((!m_bShareClip && ammo_clip_size[FIRE_SECONDARY] && !ammo_in_clip[FIRE_SECONDARY])
|| (m_bShareClip && ammo_clip_size[FIRE_PRIMARY] && !ammo_in_clip[FIRE_PRIMARY]))
&& HasAnim("secondaryfire_empty")) {
SetWeaponAnim("secondaryfire_empty", done_event);
} else {
SetWeaponAnim("secondaryfire", done_event);
}
}
}
//======================
//Weapon::DetachGun
//======================
void Weapon::DetachGun(void)
{
if (attached) {
if (m_iZoom && owner && owner->IsSubclassOfPlayer()) {
Player *p = (Player *)owner.Pointer();
p->ZoomOff();
}
StopSound(CHAN_WEAPONIDLE);
attached = false;
detach();
hideModel();
}
}
//======================
//Weapon::AttachGun
//======================
void Weapon::AttachGun(weaponhand_t hand, qboolean holstering)
{
int tag_num;
Vector vOffset;
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;
current_attachToTag = holster_attachToTag;
vOffset = holsterOffset;
setAngles(holsterAngles);
setScale(holsterScale);
} else {
if (lastValid) {
// Restore the last
setScale(lastScale);
setAngles(lastAngles);
lastValid = qfalse;
}
vOffset.setXYZ(0, 0, 0);
switch (hand) {
case WEAPON_MAIN:
current_attachToTag = attachToTag_main;
break;
case WEAPON_OFFHAND:
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, qtrue, vOffset);
showModel();
setOrigin();
} else {
warning(
"Weapon::AttachGun",
"Attachment of weapon to tag \"%s\": Tag Not Found\n",
this->current_attachToTag.c_str()
);
}
}
if (m_bAutoZoom && owner->IsSubclassOfPlayer()) {
Player *p = (Player *)owner.Pointer();
p->ToggleZoom(m_iZoom);
}
}
//======================
//Weapon::AttachToHand
//======================
void Weapon::AttachToHand(Event *ev)
{
str tag;
if (!owner || !attached) {
return;
}
weaponhand_t hand = WeaponHandNameToNum(ev->GetString(1));
if (hand == WEAPON_ERROR) {
return;
}
if (hand == WEAPON_OFFHAND) {
tag = attachToTag_offhand;
} else {
tag = attachToTag_main;
}
current_attachToTag = tag;
int tagnum = gi.Tag_NumForName(owner->edict->tiki, tag);
if (tagnum < 0) {
warning(
"Weapon::AttachToHand",
"Attachment of weapon '%s' to tag \"%s\": Tag Not Found\n",
getName().c_str(),
tag.c_str()
);
} else {
attach(owner->entnum, tagnum);
setOrigin();
}
}
//======================
//Weapon::SetCantPartialReload
//======================
void Weapon::SetCantPartialReload(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
m_bCanPartialReload = qfalse;
}
//======================
//Weapon::SetDMCantPartialReload
//======================
void Weapon::SetDMCantPartialReload(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
m_bCanPartialReload = qfalse;
}
//======================
//Weapon::AddAdditionalStartAmmo
//======================
void Weapon::AddAdditionalStartAmmo(Event *ev)
{
m_additionalStartAmmoTypes.AddObject(ev->GetString(1));
m_additionalStartAmmoAmounts.AddObject(ev->GetInteger(2));
}
//======================
//Weapon::AddStartItem
//======================
void Weapon::AddStartItem(Event *ev)
{
m_startItems.AddObject(ev->GetString(1));
}
//======================
//Weapon::GiveStartingAmmoToOwner
//======================
void Weapon::GiveStartingAmmoToOwner(Event *ev)
{
str ammotype;
int mode;
int i;
if (!owner) {
return;
}
// Give the player the starting ammo
for (mode = FIRE_PRIMARY; mode < MAX_FIREMODES; mode++) {
ammotype = GetAmmoType((firemode_t)(mode));
if (ammotype.length()) {
int start_ammo = this->GetStartAmmo((firemode_t)mode);
if (ammo_clip_size[mode]) {
int ammo = ammo_clip_size[mode] - ammo_in_clip[mode];
if (ammo > 0) {
if (ammo < start_ammo) {
start_ammo -= ammo;
ammo_in_clip[mode] = ammo + ammo_in_clip[mode];
} else {
ammo_in_clip[mode] = start_ammo + ammo_in_clip[mode];
start_ammo = 0;
}
}
}
if (start_ammo) {
owner->GiveAmmo(ammotype, start_ammo);
}
}
}
if (m_additionalStartAmmoTypes.NumObjects()) {
for (i = 1; i <= m_additionalStartAmmoTypes.NumObjects(); i++) {
const str& type = m_additionalStartAmmoTypes.ObjectAt(i);
int startAmmoAmount = m_additionalStartAmmoAmounts.ObjectAt(i);
if (type.length() && startAmmoAmount) {
owner->GiveAmmo(type, startAmmoAmount);
}
}
m_additionalStartAmmoTypes.ClearObjectList();
m_additionalStartAmmoAmounts.ClearObjectList();
}
if (m_startItems.NumObjects()) {
for (i = 1; i <= m_startItems.NumObjects(); i++) {
const str& itemName = m_startItems.ObjectAt(i);
if (itemName.length()) {
owner->giveItem(itemName);
}
}
m_startItems.ClearObjectList();
}
}
//======================
//Weapon::IsSecondaryWeapon
//======================
qboolean Weapon::IsSecondaryWeapon(void)
{
return (weapon_class & WEAPON_CLASS_SECONDARY);
}
//======================
//Weapon::PickupWeapon
//======================
void Weapon::PickupWeapon(Event *ev)
{
Sentient *sen;
Entity *other;
qboolean hasweapon;
qboolean hasclass;
int iGiveAmmo;
str realname;
other = ev->GetEntity(1);
assert(other);
if (!other->IsSubclassOfSentient()) {
return;
}
sen = (Sentient *)other;
// If this is the last owner, check to see if he can pick it up
if ((sen == last_owner) && (level.time < last_owner_trigger_time)) {
return;
}
hasweapon = sen->HasItem(item_name);
hasclass = sen->HasWeaponClass(weapon_class);
if ((g_gametype->integer || g_realismmode->integer) && !hasclass && !IsSecondaryWeapon()
&& sen->HasPrimaryWeapon()) {
// Make sure the sentient doesn't have a primary weapon on DM modes
return;
}
if ((g_gametype->integer || g_realismmode->integer) && other->IsSubclassOfPlayer()
&& (weapon_class & WEAPON_CLASS_GRENADE) && !hasweapon && hasclass) {
hasclass = false;
}
if (!hasweapon && !hasclass) {
if (other->IsSubclassOfPlayer()) {
gi.SendServerCommand(
other->edict - g_entities,
"print \"" HUD_MESSAGE_YELLOW "%s\n\"",
gi.LV_ConvertString(va("Picked Up %s", item_name.c_str()))
);
}
if (!(spawnflags & DROPPED_PLAYER_ITEM) && !(spawnflags & DROPPED_ITEM)) {
ItemPickup(other);
return;
}
if (!Pickupable(other)) {
return;
}
setMoveType(MOVETYPE_NONE);
setSolidType(SOLID_NOT);
hideModel();
velocity = vec_zero;
avelocity = vec_zero;
CancelEventsOfType(EV_Remove);
CancelEventsOfType(EV_Weapon_FallingAngleAdjust);
DetachFromOwner();
current_attachToTag = "";
lastValid = qfalse;
edict->s.tag_num = -1;
edict->s.attach_use_angles = qfalse;
VectorClear(edict->s.attach_offset);
setOrigin(vec_zero);
setAngles(vec_zero);
SetOwner(sen);
sen->AddItem(this);
sen->ReceivedItem(this);
iGiveAmmo = startammo[FIRE_PRIMARY];
Sound(sPickupSound);
} else {
bool bSameAmmo[MAX_FIREMODES] = {false};
if (sen->AmmoCount(ammo_type[FIRE_PRIMARY]) == sen->MaxAmmoCount(ammo_type[FIRE_PRIMARY])) {
bSameAmmo[FIRE_PRIMARY] = true;
if (ammo_type[0] == ammo_type[1]) {
bSameAmmo[FIRE_SECONDARY] = true;
}
}
if (!bSameAmmo[FIRE_SECONDARY]) {
if (sen->AmmoCount(ammo_type[FIRE_SECONDARY]) == sen->MaxAmmoCount(ammo_type[FIRE_SECONDARY])) {
bSameAmmo[FIRE_SECONDARY] = true;
}
}
if (bSameAmmo[FIRE_PRIMARY] && bSameAmmo[FIRE_SECONDARY]) {
return;
}
if (bSameAmmo[FIRE_PRIMARY]) {
startammo[FIRE_PRIMARY] = 0;
}
if (bSameAmmo[FIRE_SECONDARY]) {
startammo[FIRE_SECONDARY] = 0;
}
iGiveAmmo = ammo_in_clip[FIRE_PRIMARY] + startammo[FIRE_PRIMARY];
if (!iGiveAmmo && !startammo[FIRE_SECONDARY]) {
// Fixed in OPM
// Don't let the sentient pick the ammo up if it already has the weapon in inventory
// and the player would get no ammo.
// This can only be picked up as a new weapon.
return;
}
setSolidType(SOLID_NOT);
hideModel();
CancelEventsOfType(EV_Item_DropToFloor);
CancelEventsOfType(EV_Item_Respawn);
CancelEventsOfType(EV_FadeOut);
CancelEventsOfType(EV_Remove);
CancelEventsOfType(EV_Weapon_FallingAngleAdjust);
if (Respawnable()) {
PostEvent(EV_Item_Respawn, 0);
} else {
PostEvent(EV_Remove, 0);
}
Sound(m_sAmmoPickupSound);
}
if (startammo[FIRE_PRIMARY] && ammo_type[FIRE_PRIMARY].length() && other->isClient()) {
str sMessage;
const str& sAmmoType = ammo_type[FIRE_PRIMARY];
sen->GiveAmmo(sAmmoType, iGiveAmmo);
if (!sAmmoType.icmp("grenade") || !sAmmoType.icmp("agrenade")) {
if (iGiveAmmo == 1) {
sMessage = gi.LV_ConvertString("Got 1 Grenade");
} else {
sMessage = gi.LV_ConvertString(va("Got %i Grenades", iGiveAmmo));
}
} else {
sMessage = gi.LV_ConvertString(va("Got %i %s Rounds", iGiveAmmo, GetCapitalized(sAmmoType).c_str()));
}
gi.SendServerCommand(other->edict - g_entities, "print \"" HUD_MESSAGE_YELLOW "%s\n\"", sMessage.c_str());
}
if (ammo_type[FIRE_SECONDARY] != ammo_type[FIRE_PRIMARY] && str::icmp(ammo_type[FIRE_SECONDARY], "none")) {
if (startammo[FIRE_SECONDARY] && ammo_type[FIRE_SECONDARY].length() && other->isClient()) {
str sMessage;
const str& sAmmoType = ammo_type[FIRE_SECONDARY];
iGiveAmmo = startammo[FIRE_SECONDARY];
sen->GiveAmmo(sAmmoType, iGiveAmmo);
if (!sAmmoType.icmp("grenade") || !sAmmoType.icmp("agrenade")) {
if (iGiveAmmo == 1) {
sMessage = gi.LV_ConvertString("Got 1 Grenade");
} else {
sMessage = gi.LV_ConvertString(va("Got %i Grenades", iGiveAmmo));
}
} else if (!sAmmoType.icmp("riflegrenade")) {
if (iGiveAmmo == 1) {
sMessage = gi.LV_ConvertString("Got 1 Rifle Grenade");
} else {
sMessage = gi.LV_ConvertString(va("Got %i Rifle Grenades", iGiveAmmo));
}
} else {
sMessage = gi.LV_ConvertString(
va("Got %i %s Rounds", startammo[FIRE_PRIMARY], GetCapitalized(sAmmoType).c_str())
);
}
gi.SendServerCommand(other->edict - g_entities, "print \"" HUD_MESSAGE_YELLOW "%s\n\"", sMessage.c_str());
}
}
Unregister(STRING_PICKUP);
}
//======================
//Weapon::ForceIdle
//======================
void Weapon::ForceIdle(void)
{
SetWeaponIdleAnim();
// Force the weapon to the idle animation
weaponstate = WEAPON_READY;
// Fixed in OPM
// Stop the weapon from charging/cooking
CancelEventsOfType(EV_OverCooked);
CancelEventsOfType(EV_OverCooked_Warning);
charge_fraction = 0;
}
//======================
//Weapon::SetWeaponIdleAnim
//======================
void Weapon::SetWeaponIdleAnim(void)
{
if (m_bShareClip) {
if (ammo_clip_size[FIRE_PRIMARY] && !ammo_in_clip[FIRE_PRIMARY]) {
if (SetWeaponAnim("idle_empty")) {
return;
}
}
SetWeaponAnim("idle");
} else {
if (ammo_clip_size[FIRE_PRIMARY] && !ammo_in_clip[FIRE_PRIMARY]) {
if (SetWeaponAnim("idle_empty")) {
return;
}
}
}
SetWeaponAnim("idle");
}
//======================
//Weapon::SetWeaponIdleAnimEvent
//======================
void Weapon::SetWeaponIdleAnimEvent(Event *ev)
{
SetWeaponIdleAnim();
}
//======================
//Weapon::SetWeaponAnimEvent
//======================
void Weapon::SetWeaponAnimEvent(Event *ev)
{
//SetWeaponAnim(ev->GetString(1));
SetWeaponAnim(ev->GetString(1), EV_Weapon_DoneAnimating);
}
//======================
//Weapon::SetWeaponAnim
//======================
qboolean Weapon::SetWeaponAnim(const char *anim, Event *ev)
{
int animnum = gi.Anim_NumForName(edict->tiki, anim);
if (animnum == -1) {
if (ev) {
delete ev;
}
return qfalse;
}
StopAnimating(m_iAnimSlot);
RestartAnimSlot(m_iAnimSlot);
int idleanim = gi.Anim_NumForName(edict->tiki, "idle");
edict->s.frameInfo[m_iAnimSlot].index = idleanim;
m_iAnimSlot = (m_iAnimSlot + 1) % MAX_WEAPON_ANIM_SLOTS;
edict->s.frameInfo[m_iAnimSlot].index = idleanim;
if (ev) {
NewAnim(animnum, ev, m_iAnimSlot);
} else {
NewAnim(animnum, m_iAnimSlot);
}
SetOnceType(m_iAnimSlot);
RestartAnimSlot(m_iAnimSlot);
return qtrue;
}
//======================
//Weapon::SetWeaponAnim
//======================
qboolean Weapon::SetWeaponAnim(const char *anim, Event& ev)
{
Event *event = new Event(ev);
return SetWeaponAnim(anim, event);
}
//======================
//Weapon::StopWeaponAnim
//======================
void Weapon::StopWeaponAnim(void)
{
int animnum;
RestartAnimSlot(m_iAnimSlot);
StopAnimating(m_iAnimSlot);
animnum = gi.Anim_NumForName(edict->tiki, "idle");
StartAnimSlot(m_iAnimSlot, animnum, 1.f);
m_iAnimSlot = (m_iAnimSlot + 1) % MAX_WEAPON_ANIM_SLOTS;
}
//======================
//Weapon::DoneRaising
//======================
void Weapon::DoneRaising(Event *ev)
{
weaponstate = WEAPON_READY;
ForceIdle();
if (!owner) {
PostEvent(EV_Remove, 0);
return;
}
}
//======================
//Weapon::ClientFireDone
//======================
void Weapon::ClientFireDone(void)
{
// This is called when the client's firing animation is done
}
//======================
//Weapon::DoneFiring
//======================
void Weapon::DoneFiring(Event *ev)
{
// This is called when the weapon's firing animation is done
ForceIdle();
// Check to see if the auto_putaway flag is set, and the weapon is out of ammo. If so, then putaway the
// weapon.
if (!HasAmmo(FIRE_PRIMARY) && auto_putaway) {
PutAway();
}
}
//======================
//Weapon::FillAmmoClip
//======================
void Weapon::FillAmmoClip(Event *ev)
{
int amount;
int amount_used;
if (!ammo_clip_size[0] || !owner) {
return;
}
if (UnlimitedAmmo(FIRE_PRIMARY)) {
// reload directly
ammo_in_clip[FIRE_PRIMARY] = ammo_clip_size[0];
} else {
// Calc the amount the clip should get
amount = ammo_clip_size[FIRE_PRIMARY] - ammo_in_clip[FIRE_PRIMARY];
// use up the ammo from the player
amount_used = owner->UseAmmo(ammo_type[FIRE_PRIMARY], amount);
// Stick it in the clip
ammo_in_clip[FIRE_PRIMARY] = amount_used + ammo_in_clip[FIRE_PRIMARY];
}
owner->AmmoAmountInClipChanged(ammo_type[FIRE_PRIMARY], ammo_in_clip[FIRE_PRIMARY]);
SetShouldReload(qfalse);
}
//======================
//Weapon::EmptyAmmoClip
//======================
void Weapon::EmptyAmmoClip(Event *ev)
{
if (!ammo_clip_size[FIRE_PRIMARY]) {
return;
}
if (!owner) {
return;
}
owner->GiveAmmo(ammo_type[FIRE_PRIMARY], ammo_in_clip[FIRE_PRIMARY]);
ammo_in_clip[FIRE_PRIMARY] = 0;
SetShouldReload(qtrue);
owner->AmmoAmountInClipChanged(ammo_type[FIRE_PRIMARY], ammo_in_clip[FIRE_PRIMARY]);
}
//======================
//Weapon::AddToAmmoClip
//======================
void Weapon::AddToAmmoClip(Event *ev)
{
int amount;
int amount_used;
if (!ammo_clip_size[FIRE_PRIMARY] || !owner) {
return;
}
// Calc the amount the clip should get
amount = ev->GetInteger(1);
if (amount > ammo_clip_size[FIRE_PRIMARY] - ammo_in_clip[FIRE_PRIMARY]) {
amount = ammo_clip_size[FIRE_PRIMARY] - ammo_in_clip[FIRE_PRIMARY];
}
if (UnlimitedAmmo(FIRE_PRIMARY)) {
// Stick it in the clip
ammo_in_clip[FIRE_PRIMARY] = amount + ammo_in_clip[FIRE_PRIMARY];
} else {
// use up the ammo from the player
amount_used = owner->UseAmmo(ammo_type[FIRE_PRIMARY], amount);
// Stick it in the clip
ammo_in_clip[FIRE_PRIMARY] = amount_used + ammo_in_clip[FIRE_PRIMARY];
}
owner->AmmoAmountInClipChanged(ammo_type[FIRE_PRIMARY], ammo_in_clip[FIRE_PRIMARY]);
SetShouldReload(qfalse);
}
//======================
//Weapon::SetIdleState
//======================
void Weapon::SetIdleState(int state)
{
if (!owner) {
return;
}
switch (state) {
case 0:
default:
SetWeaponAnim("idle0");
break;
case 1:
SetWeaponAnim("idle1");
break;
case 2:
SetWeaponAnim("idle2");
break;
}
}
//======================
//Weapon::StartReloading
//======================
void Weapon::StartReloading(void)
{
if (!ammo_clip_size[0] || !owner) {
return;
}
if (SetWeaponAnim("reload", EV_Weapon_DoneReloading)) {
weaponstate = WEAPON_RELOADING;
} else {
ProcessEvent(EV_Weapon_FillClip);
ProcessEvent(EV_Weapon_DoneReloading);
}
m_fFireSpreadMult[FIRE_PRIMARY] = 0;
}
//======================
//Weapon::DoneReloading
//======================
void Weapon::DoneReloading(Event *ev)
{
SetShouldReload(qfalse);
weaponstate = WEAPON_READY;
// Added in OPM
// Set to idle when done reloading
SetWeaponIdleAnim();
}
//======================
//Weapon::CheckReload
//======================
qboolean Weapon::CheckReload(firemode_t mode)
{
// Check to see if the weapon needs to be reloaded
assert((mode >= 0) && (mode < MAX_FIREMODES));
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if (putaway) {
return false;
}
if (ammo_in_clip[mode] < ammo_clip_size[mode] && AmmoAvailable(mode)
&& (m_bCanPartialReload || ammo_in_clip[mode] <= 0)) {
return true;
}
return false;
}
//======================
//Weapon::SetShouldReload
//======================
void Weapon::SetShouldReload(qboolean should_reload)
{
m_bShouldReload = should_reload;
}
//======================
//Weapon::ShouldReload
//======================
qboolean Weapon::ShouldReload(void)
{
if (m_bShouldReload) {
return qtrue;
} else {
if (ammo_clip_size[FIRE_PRIMARY] && !ammo_in_clip[FIRE_PRIMARY] && AmmoAvailable(FIRE_PRIMARY)) {
return qtrue;
}
}
return qfalse;
}
//======================
//Weapon::Idle
//======================
void Weapon::Idle(Event *ev)
{
ForceIdle();
}
//======================
//Weapon::IdleInit
//======================
void Weapon::IdleInit(Event *ev)
{
for (int i = 7; i >= 0; i--) {
SetWeaponAnim("idle");
}
weaponstate = WEAPON_READY;
}
//======================
//Weapon::GetMaxRange
//======================
float Weapon::GetMaxRange(void)
{
return maxrange;
}
//======================
//Weapon::GetMinRange
//======================
float Weapon::GetMinRange(void)
{
return minrange;
}
//======================
//Weapon::SetMaxRangeEvent
//======================
void Weapon::SetMaxRangeEvent(Event *ev)
{
maxrange = ev->GetFloat(1);
}
//======================
//Weapon::SetMinRangeEvent
//======================
void Weapon::SetMinRangeEvent(Event *ev)
{
minrange = ev->GetFloat(1);
}
//======================
//Weapon::NotDroppableEvent
//======================
void Weapon::NotDroppableEvent(Event *ev)
{
notdroppable = true;
}
//======================
//Weapon::SetMaxRange
//======================
void Weapon::SetMaxRange(float val)
{
maxrange = val;
}
//======================
//Weapon::SetMinRange
//======================
void Weapon::SetMinRange(float val)
{
minrange = val;
}
//======================
//Weapon::WeaponSound
//======================
void Weapon::WeaponSound(Event *ev)
{
// Broadcasting a sound can be time consuming. Only do it once in a while on really fast guns.
if (nextweaponsoundtime > level.time) {
if (owner) {
owner->BroadcastAIEvent(AI_EVENT_WEAPON_FIRE);
} else {
BroadcastAIEvent(AI_EVENT_WEAPON_FIRE);
}
return;
}
if (owner) {
owner->ProcessEvent(*ev);
} else {
Item::BroadcastAIEvent(AI_EVENT_WEAPON_FIRE);
}
// give us some breathing room
nextweaponsoundtime = level.time + 0.4;
}
//======================
//Weapon::Removable
//======================
qboolean Weapon::Removable(void)
{
if (((int)(dmflags->integer) & DF_WEAPONS_STAY) && !(spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM))) {
return false;
} else {
return true;
}
}
//======================
//Weapon::Pickupable
//======================
qboolean Weapon::Pickupable(Entity *other)
{
Sentient *sen;
if (!other->IsSubclassOfSentient()) {
return false;
} else if (!other->isClient()) {
return false;
}
sen = (Sentient *)other;
// If we have the weapon and weapons stay, then don't pick it up
if (((int)(dmflags->integer) & DF_WEAPONS_STAY) && !(spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM))) {
Weapon *weapon;
weapon = (Weapon *)sen->FindItem(getName());
if (weapon) {
return false;
}
}
return true;
}
//======================
//Weapon::AutoChange
//======================
qboolean Weapon::AutoChange(void)
{
return true;
}
//======================
//Weapon::ClipAmmo
//======================
int Weapon::ClipAmmo(firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
mode = m_bShareClip ? FIRE_PRIMARY : mode;
if (ammo_clip_size[mode]) {
return ammo_in_clip[mode];
} else {
return -1;
}
}
//======================
//Weapon::SetFireDelay
//======================
void Weapon::SetFireDelay(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
fire_delay[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetDMFireDelay
//======================
void Weapon::SetDMFireDelay(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
fire_delay[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::FireDelay
//======================
float Weapon::FireDelay(firemode_t mode)
{
return fire_delay[mode];
}
//======================
//Weapon::IsDroppable
//======================
qboolean Weapon::IsDroppable(void)
{
if (notdroppable) {
return false;
} else {
return true;
}
}
//======================
//Weapon::SetFireType
//======================
void Weapon::SetFireType(Event *ev)
{
str ftype;
ftype = ev->GetString(1);
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
if (!ftype.icmp("projectile")) {
firetype[firemodeindex] = FT_PROJECTILE;
} else if (!ftype.icmp("bullet")) {
firetype[firemodeindex] = FT_BULLET;
} else if (!ftype.icmp("fakebullet")) {
firetype[firemodeindex] = FT_FAKEBULLET;
} else if (!ftype.icmp("melee")) {
firetype[firemodeindex] = FT_MELEE;
} else if (!ftype.icmp("special_projectile")) {
firetype[firemodeindex] = FT_SPECIAL_PROJECTILE;
} else if (!ftype.icmp("clickitem")) {
firetype[firemodeindex] = FT_CLICKITEM;
} else if (!ftype.icmp("heavy")) {
firetype[firemodeindex] = FT_HEAVY;
} else if (!ftype.icmp("landmine")) {
firetype[firemodeindex] = FT_LANDMINE;
} else if (!ftype.icmp("defuse")) {
firetype[firemodeindex] = FT_DEFUSE;
} else if (!ftype.icmp("none")) {
firetype[firemodeindex] = FT_NONE;
} else {
warning("Weapon::SetFireType", "unknown firetype: %s\n", ftype.c_str());
}
}
//======================
//Weapon::GetFireType
//======================
firetype_t Weapon::GetFireType(firemode_t mode)
{
return firetype[mode];
}
firemode_t Weapon::GetFireMode()
{
return firemodeindex;
}
//======================
//Weapon::SetProjectile
//======================
void Weapon::SetProjectile(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
projectileModel[firemodeindex] = ev->GetString(1);
CacheResource(projectileModel[firemodeindex].c_str());
}
//======================
//Weapon::SetDMProjectile
//======================
void Weapon::SetDMProjectile(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
projectileModel[firemodeindex] = ev->GetString(1);
CacheResource(projectileModel[firemodeindex].c_str());
}
//======================
//Weapon::SetBulletDamage
//======================
void Weapon::SetBulletDamage(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletdamage[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletLarge
//======================
void Weapon::SetBulletLarge(Event *ev)
{
bulletlarge[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::SetBulletLarge
//======================
void Weapon::SetTracerSpeed(Event *ev)
{
tracerspeed[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetDMBulletDamage
//======================
void Weapon::SetDMBulletDamage(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletdamage[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletKnockback
//======================
void Weapon::SetBulletKnockback(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletknockback[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetDMBulletKnockback
//======================
void Weapon::SetDMBulletKnockback(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletknockback[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletThroughWood
//======================
void Weapon::SetBulletThroughWood(Event *ev)
{
bulletthroughwood[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletThroughMetal
//======================
void Weapon::SetBulletThroughMetal(Event *ev)
{
bulletthroughmetal[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletRange
//======================
void Weapon::SetBulletRange(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletrange[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetDMBulletRange
//======================
void Weapon::SetDMBulletRange(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletrange[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetRange
//======================
void Weapon::SetRange(Event *ev)
{
SetBulletRange(ev);
}
//======================
//Weapon::SetBulletCount
//======================
void Weapon::SetBulletCount(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletcount[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetDMBulletCount
//======================
void Weapon::SetDMBulletCount(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletcount[firemodeindex] = ev->GetFloat(1);
}
//======================
//Weapon::SetBulletSpread
//======================
void Weapon::SetBulletSpread(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletspread[firemodeindex].x = ev->GetFloat(1);
bulletspread[firemodeindex].y = ev->GetFloat(2);
if (ev->NumArgs() > 2) {
bulletspreadmax[firemodeindex].x = ev->GetFloat(3);
bulletspreadmax[firemodeindex].y = ev->GetFloat(4);
}
}
//======================
//Weapon::SetDMBulletSpread
//======================
void Weapon::SetDMBulletSpread(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
bulletspread[firemodeindex].x = ev->GetFloat(1);
bulletspread[firemodeindex].y = ev->GetFloat(2);
if (ev->NumArgs() > 2) {
bulletspreadmax[firemodeindex].x = ev->GetFloat(3);
bulletspreadmax[firemodeindex].y = ev->GetFloat(4);
}
}
//======================
//Weapon::SetZoomSpreadMult
//======================
void Weapon::SetZoomSpreadMult(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
m_fZoomSpreadMult = ev->GetFloat(1);
}
//======================
//Weapon::SetDMZoomSpreadMult
//======================
void Weapon::SetDMZoomSpreadMult(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
m_fZoomSpreadMult = ev->GetFloat(1);
}
//======================
//Weapon::SetZoomSpreadMult
//======================
void Weapon::SetFireSpreadMult(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
m_fFireSpreadMultAmount[firemodeindex] = ev->GetFloat(1);
m_fFireSpreadMultFalloff[firemodeindex] = ev->GetFloat(2);
m_fFireSpreadMultCap[firemodeindex] = ev->GetFloat(3);
m_fFireSpreadMultTimeCap[firemodeindex] = ev->GetFloat(4);
}
//======================
//Weapon::SetDMZoomSpreadMult
//======================
void Weapon::SetDMFireSpreadMult(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
m_fFireSpreadMultAmount[firemodeindex] = ev->GetFloat(1);
m_fFireSpreadMultFalloff[firemodeindex] = ev->GetFloat(2);
m_fFireSpreadMultCap[firemodeindex] = ev->GetFloat(3);
m_fFireSpreadMultTimeCap[firemodeindex] = ev->GetFloat(4);
}
//======================
//Weapon::SetTracerFrequency
//======================
void Weapon::SetTracerFrequency(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
tracerfrequency[firemodeindex] = ev->GetInteger(1);
}
//======================
//Weapon::Secondary
//======================
void Weapon::Secondary(Event *ev)
{
int i;
Event *altev = new Event(ev->GetToken(1));
firemodeindex = FIRE_SECONDARY;
for (i = 2; i <= ev->NumArgs(); i++) {
altev->AddToken(ev->GetToken(i));
}
ProcessEvent(altev);
firemodeindex = FIRE_PRIMARY;
}
//====================
//Weapon::AutoAim
//====================
void Weapon::AutoAim(Event *ev)
{
autoaim = true;
}
//====================
//Weapon::Crosshair
//====================
void Weapon::Crosshair(Event *ev)
{
crosshair = ev->GetBoolean(1);
}
//====================
//Weapon::DMCrosshair
//====================
void Weapon::DMCrosshair(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
crosshair = ev->GetBoolean(1);
}
//====================
//Weapon::MainAttachToTag
//====================
void Weapon::MainAttachToTag(Event *ev)
{
attachToTag_main = ev->GetString(1);
}
//====================
//Weapon::OffHandAttachToTag
//====================
void Weapon::OffHandAttachToTag(Event *ev)
{
attachToTag_offhand = ev->GetString(1);
}
//====================
//Weapon::HolsterAttachToTag
//====================
void Weapon::HolsterAttachToTag(Event *ev)
{
holster_attachToTag = ev->GetString(1);
}
//====================
//Weapon::SetHolsterOffset
//====================
void Weapon::SetHolsterOffset(Event *ev)
{
holsterOffset = ev->GetVector(1);
}
//====================
//Weapon::SetHolsterAngles
//====================
void Weapon::SetHolsterAngles(Event *ev)
{
holsterAngles = ev->GetVector(1);
}
//====================
//Weapon::SetHolsterScale
//====================
void Weapon::SetHolsterScale(Event *ev)
{
holsterScale = ev->GetFloat(1);
}
//====================
//Weapon::SetQuiet
//====================
void Weapon::SetQuiet(Event *ev)
{
quiet[firemodeindex] = true;
}
//====================
//Weapon::SetLoopFire
//====================
void Weapon::SetLoopFire(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
loopfire[firemodeindex] = true;
}
//======================
//Weapon::SetMeansOfDeath
//======================
void Weapon::SetMeansOfDeath(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
meansofdeath[firemodeindex] = (meansOfDeath_t)MOD_string_to_int(ev->GetString(1));
}
//======================
//Weapon::GetMeansOfDeath
//======================
meansOfDeath_t Weapon::GetMeansOfDeath(firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
if ((mode >= 0) && (mode < MAX_FIREMODES)) {
return meansofdeath[mode];
} else {
warning("Weapon::GetMeansOfDeath", "Invalid mode %d\n", mode);
return MOD_NONE;
}
}
//======================
//Weapon::SetAimTarget
//======================
void Weapon::SetAimTarget(Entity *ent)
{
aim_target = ent;
}
//======================
//Weapon::WorldHitSpawn
//======================
void Weapon::WorldHitSpawn(firemode_t mode, Vector origin, Vector angles, float life) {}
//======================
//Weapon::SetWorldHitSpawn
//======================
void Weapon::SetWorldHitSpawn(Event *ev)
{
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
worldhitspawn[firemodeindex] = ev->GetString(1);
}
//======================
//Weapon::MakeNoise
//======================
void Weapon::MakeNoise(Event *ev)
{
float radius = 500;
qboolean force = false;
if (ev->NumArgs() > 0) {
radius = ev->GetFloat(1);
}
if (ev->NumArgs() > 1) {
force = ev->GetBoolean(2);
}
if (attached && (next_noise_time <= level.time || force)) {
BroadcastAIEvent(AI_EVENT_MISC, radius);
next_noise_time = level.time + 1;
}
}
//======================
//Weapon::ShowInfo
//======================
void Weapon::ShowInfo(float fDot, float fDist) {}
//======================
//Weapon::FallingAngleAdjust
//======================
void Weapon::FallingAngleAdjust(Event *ev)
{
Vector vTmp;
Vector vDir;
trace_t trace;
if (owner) {
return;
}
if (groundentity && (groundentity->entity == world || groundentity->r.bmodel)) {
Vector vEnd = origin - Vector(0, 0, 128);
trace = G_Trace(
origin, vec_zero, vec_zero, vEnd, last_owner, edict->clipmask, qfalse, "Weapon::FallingAngleAdjust"
);
if (trace.fraction < 1.0f) {
Vector vAng = Vector(0, angles[1], 0);
Vector vCross;
/*vAng.AngleVectorsLeft( &vDir );
CrossProduct( vAng, vDir, vCross );
CrossProduct( vDir, vCross, vAng );
vAng = vAng.toAngles();*/
if (angles[2] <= 0.0f) {
vAng[2] = anglemod(vAng[2] - 90.0f);
} else {
vAng[2] = anglemod(vAng[2] + 90.0f);
}
setAngles(vAng);
}
if (weapon_class >= 0) {
Sound(G_WeaponClassNumToName(weapon_class) + "_drop");
}
return;
}
angles[0] = anglemod(angles[0]);
if (angles[0] > 180) {
angles[0] -= 180;
}
if (fabs(angles[0]) >= 90) {
if (angles[0] > 0) {
angles[0] += level.frametime * 160;
if (angles[0] > 180) {
angles[0] = 180;
}
} else {
angles[0] -= level.frametime * 160;
if (angles[0] < -180) {
angles[0] = 180;
}
}
} else {
if (angles[0] > 0.0f) {
angles[0] -= level.frametime * 160;
if (angles[0] < 0) {
angles[0] = 0;
}
} else {
angles[0] += level.frametime * 160;
if (angles[0] > 0) {
angles[0] = 0;
}
}
}
angles[2] = anglemod(angles[2]);
if (angles[2] > 180) {
angles[2] -= 180;
}
if (angles[2] < 0) {
if (angles[2] > -90) {
angles[2] -= level.frametime * 160;
if (angles[2] < -90) {
angles[2] = -90;
}
} else {
angles[2] += level.frametime * 160;
if (angles[2] > -90) {
angles[2] = -90;
}
}
} else {
if (angles[2] > 90.0f) {
angles[2] -= level.frametime * 160;
if (angles[2] < 90) {
angles[2] = 90;
}
} else {
angles[2] += level.frametime * 160;
if (angles[2] > 90) {
angles[2] = 90;
}
}
}
angles[0] = anglemod(angles[0]);
angles[2] = anglemod(angles[2]);
setAngles(angles);
PostEvent(EV_Weapon_FallingAngleAdjust, level.frametime);
}
//======================
//Weapon::GetUseCrosshair
//======================
qboolean Weapon::GetUseCrosshair() const
{
if (g_protocol < protocol_e::PROTOCOL_MOHTA_MIN) {
// Always show the crosshair in 1.11 and below
return qtrue;
}
if (m_fMaxFireMovement >= 1.f) {
return crosshair;
}
if (!owner || (owner->velocity.lengthXY() / sv_runspeed->value) <= (m_fMaxFireMovement * m_fMovementSpeed)) {
return crosshair;
}
return qfalse;
}
//======================
//Weapon::SetAIRange
//======================
void Weapon::SetAIRange(Event *ev)
{
str s = ev->GetString(1);
if (!s.icmp("short")) {
mAIRange = RANGE_SHORT;
} else if (!s.icmp("medium")) {
mAIRange = RANGE_MEDIUM;
} else if (!s.icmp("long")) {
mAIRange = RANGE_LONG;
} else if (!s.icmp("sniper")) {
mAIRange = RANGE_SNIPER;
} else {
warning("Weapon::SetAIRange", "unknown range: %s. Should be short, medium, long, or sniper\n", s.c_str());
}
}
//======================
//Weapon::SetViewKick
//======================
void Weapon::SetViewKick(Event *ev)
{
float pitchmin, pitchmax;
float yawmin, yawmax;
assert((firemodeindex >= 0) && (firemodeindex < MAX_FIREMODES));
pitchmin = ev->GetFloat(1);
pitchmax = ev->GetFloat(2);
if (pitchmin <= pitchmax) {
viewkickmin[firemodeindex][0] = pitchmin;
viewkickmax[firemodeindex][0] = pitchmax;
} else {
viewkickmin[firemodeindex][0] = pitchmax;
viewkickmax[firemodeindex][0] = pitchmin;
}
if (ev->NumArgs() > 2) {
yawmin = ev->GetFloat(3);
yawmax = ev->GetFloat(4);
if (pitchmin <= pitchmax) {
viewkickmin[firemodeindex][1] = yawmin;
viewkickmax[firemodeindex][1] = yawmax;
} else {
viewkickmin[firemodeindex][1] = yawmax;
viewkickmax[firemodeindex][1] = yawmin;
}
}
}
//======================
//Weapon::SetMovementSpeed
//======================
void Weapon::SetMovementSpeed(Event *ev)
{
if (g_protocol <= protocol_e::PROTOCOL_MOH && g_gametype->integer != GT_SINGLE_PLAYER) {
return;
}
m_fMovementSpeed = ev->GetFloat(1);
}
//======================
//Weapon::SetDMMovementSpeed
//======================
void Weapon::SetDMMovementSpeed(Event *ev)
{
if (g_gametype->integer == GT_SINGLE_PLAYER) {
return;
}
m_fMovementSpeed = ev->GetFloat(1);
}
//======================
//Weapon::SetMaxFireMovement
//======================
void Weapon::SetMaxFireMovement(Event *ev)
{
m_fMaxFireMovement = ev->GetFloat(1);
if (m_fMaxFireMovement > 1.0) {
// Cap at 1.0
m_fMaxFireMovement = 1.0;
}
}
//======================
//Weapon::SetZoomMovement
//======================
void Weapon::SetZoomMovement(Event *ev)
{
m_fZoomMovement = ev->GetFloat(1);
if (m_fZoomMovement > 1.0) {
// Cap at 1.0
m_fZoomMovement = 1.0;
}
}
//======================
//Weapon::EventAmmoPickupSound
//======================
void Weapon::EventAmmoPickupSound(Event *ev)
{
m_sAmmoPickupSound = ev->GetString(1);
}
//======================
//Weapon::EventNoAmmoSound
//======================
void Weapon::EventNoAmmoSound(Event *ev)
{
m_NoAmmoSound = ev->GetString(1);
}
//======================
//Weapon::EventMaxMovementSound
//======================
void Weapon::EventMaxMovementSound(Event *ev)
{
m_sMaxMovementSound = ev->GetString(1);
}
//======================
//Weapon::SetNumFireAnims
//======================
void Weapon::SetNumFireAnims(Event *ev)
{
m_iNumFireAnims = ev->GetInteger(1);
}
//======================
//Weapon::SetWeaponSubtype
//======================
void Weapon::SetWeaponSubtype(Event *ev)
{
m_iWeaponSubtype = ev->GetInteger(1);
}
//======================
//Weapon::SetCookTime
//======================
void Weapon::SetCookTime(Event *ev)
{
m_fCookTime = ev->GetFloat(1);
}
//======================
//Weapon::SetCurrentFireAnim
//======================
void Weapon::SetCurrentFireAnim(Event *ev)
{
m_iCurrentFireAnim = ev->GetInteger(1);
}
//======================
//Weapon::SetSecondaryAmmoInHud
//======================
void Weapon::SetSecondaryAmmoInHud(Event *ev)
{
m_bSecondaryAmmoInHud = qtrue;
}
//======================
//Weapon::SetRank
//======================
void Weapon::SetRank(int order, int rank)
{
this->order = order;
this->rank = rank;
}
//======================
//Weapon::GetRank
//======================
int Weapon::GetRank(void)
{
return rank;
}
//======================
//Weapon::GetOrder
//======================
int Weapon::GetOrder(void)
{
return order;
}
//======================
//Weapon::GetWeaponClass
//======================
int Weapon::GetWeaponClass(void) const
{
return weapon_class;
}
//======================
//Weapon::GetWeaponGroup
//======================
const_str Weapon::GetWeaponGroup(void) const
{
return m_csWeaponGroup;
};
//======================
//Weapon::SetItemSlot
//======================
void Weapon::SetItemSlot(int slot)
{
weapon_class &= ~WEAPON_CLASS_ITEM_SLOT_BITS;
weapon_class |= slot & WEAPON_CLASS_ITEM_SLOT_BITS;
}
//======================
//Weapon::GetPutaway
//======================
qboolean Weapon::GetPutaway(void)
{
return putaway;
}
//======================
//Weapon::SetPutAway
//======================
void Weapon::SetPutAway(qboolean p)
{
putaway = p;
}
//======================
//Weapon::GetState
//======================
weaponstate_t Weapon::GetState(void)
{
return weaponstate;
}
//======================
//Weapon::SpecialFireProjectile
//======================
void Weapon::SpecialFireProjectile(
Vector pos, Vector forward, Vector right, Vector up, Entity *owner, str projectileModel, float charge_fraction
)
{
// Does nothing
}
//======================
//Weapon::GetCurrentAttachToTag
//======================
str Weapon::GetCurrentAttachToTag(void)
{
return current_attachToTag;
}
//======================
//Weapon::SetCurrentAttachToTag
//======================
void Weapon::SetCurrentAttachToTag(str s)
{
current_attachToTag = s;
}
//======================
//Weapon::GetHolsterTag
//======================
str Weapon::GetHolsterTag(void)
{
return holster_attachToTag;
}
//======================
//Weapon::GetUseNoAmmo
//======================
qboolean Weapon::GetUseNoAmmo(void)
{
return use_no_ammo;
}
//====================
//Weapon::GetZoom
//====================
int Weapon::GetZoom(void)
{
return m_iZoom;
}
//====================
//Weapon::GetAutoZoom
//====================
qboolean Weapon::GetAutoZoom(void)
{
return m_bAutoZoom;
}
//======================
//Weapon::GetMovementSpeed
//======================
float Weapon::GetMovementSpeed() const
{
return m_fMovementSpeed;
}
//======================
//Weapon::IsCarryableTurret
//======================
bool Weapon::IsCarryableTurret() const
{
return false;
}
//======================
//Weapon::GetStartAmmo
//======================
int Weapon::GetStartAmmo(firemode_t mode)
{
assert((mode >= 0) && (mode < MAX_FIREMODES));
return startammo[mode];
}
//======================
//Weapon::IsSemiAuto
//======================
qboolean Weapon::IsSemiAuto(void)
{
return m_bSemiAuto;
}
//======================
//Weapon::ForceState
//======================
void Weapon::ForceState(weaponstate_t state)
{
weaponstate = state;
}
//======================
//Weapon::MuzzleClear
//======================
qboolean Weapon::MuzzleClear(void)
{
return qtrue;
}
//======================
//Weapon::GetBulletRange
//======================
float Weapon::GetBulletRange(firemode_t mode)
{
return bulletrange[mode];
}
//======================
//Weapon::GetSpreadFactor
//======================
float Weapon::GetSpreadFactor(firemode_t mode)
{
float fSpreadFactor;
if (owner && owner->client) {
Player *player = (Player *)owner.Pointer();
fSpreadFactor = player->velocity.length() / sv_runspeed->integer;
if (fSpreadFactor > 1.0f) {
fSpreadFactor = 1.0f;
}
} else {
fSpreadFactor = 0.5f;
}
return fSpreadFactor;
}
//======================
//Weapon::GetChargeFraction
//======================
float Weapon::GetChargeFraction(void) const
{
return charge_fraction;
}
//======================
//Weapon::GetCurrentFireSpreadMult
//======================
float Weapon::GetCurrentFireSpreadMult(firemode_t mode) const
{
const float fTime = level.time - m_fFireSpreadMultTime[mode];
if (m_fFireSpreadMultAmount[mode] != 0.0f) {
if (fTime > m_fFireSpreadMultTimeCap[mode]) {
return 0;
}
const float fDecay = fTime * m_fFireSpreadMultFalloff[mode];
if (m_fFireSpreadMult[mode] <= 0.0f) {
if (m_fFireSpreadMult[mode] > 0) {
return 0;
}
return m_fFireSpreadMult[mode] - fDecay;
} else {
if (m_fFireSpreadMult[mode] < 0.0f) {
return 0;
}
return m_fFireSpreadMult[mode] - fDecay;
}
}
if (fTime >= m_fFireSpreadMultTimeCap[mode]) {
return 0;
}
return m_fFireSpreadMult[mode];
}
//======================
//Weapon::GetCurrentFireSpreadMultTime
//======================
float Weapon::GetCurrentFireSpreadMultTime(firemode_t mode) const
{
return m_fFireSpreadMultTime[mode];
}
//======================
//Weapon::GetFireSpreadMultAmount
//======================
float Weapon::GetFireSpreadMultAmount(firemode_t mode) const
{
return m_fFireSpreadMultAmount[mode];
}
//======================
//Weapon::GetFireSpreadMultFalloff
//======================
float Weapon::GetFireSpreadMultFalloff(firemode_t mode) const
{
return m_fFireSpreadMultFalloff[mode];
}
//======================
//Weapon::GetFireSpreadMultCap
//======================
float Weapon::GetFireSpreadMultCap(firemode_t mode) const
{
return m_fFireSpreadMultCap[mode];
}
//======================
//Weapon::GetFireSpreadMultTimecap
//======================
float Weapon::GetFireSpreadMultTimeCap(firemode_t mode) const
{
return m_fFireSpreadMultTimeCap[mode];
}
//======================
//Weapon::GetScriptOwner
//======================
Listener *Weapon::GetScriptOwner(void)
{
return owner;
}
//======================
//Weapon::DoneAnimating
//======================
void Weapon::DoneAnimating(Event *ev)
{
// Added in OPM
// Set to idle when done reloading
SetWeaponIdleAnim();
}
//======================
//Weapon::GetMaxFireMovementMult
//======================
void Weapon::EventGetPutaway(Event *ev)
{
// Added in OPM
// Set to idle when done reloading
ev->AddInteger(GetPutaway());
}
//======================
//Weapon::GetMaxFireMovementMult
//======================
float Weapon::GetMaxFireMovementMult() const
{
return m_fMaxFireMovement * m_fMovementSpeed;
}
//======================
//Weapon::GetMaxFireMovementMult
//======================
float Weapon::GetZoomMovement() const
{
return m_fZoomMovement * m_fMovementSpeed;
}
//======================
//Weapon::GetMaxFireMovement
//======================
float Weapon::GetMaxFireMovement() const
{
return m_fMaxFireMovement;
}