Weapon script
While it is entirely possible to define a weapon's properties entirely in C++, Valve tend to use weapon scripts instead. These allow changes to be applied by a server restart, with no need to shut the game down and recompile its code binaries.
This provides a few advantages to the end-user and developer, respectively:
- By having code in the public realm, end-users can modify certain aspects to their liking. If you wish to support mods via this method in your own mod, giving the user more options will likely be welcomed.
- It allows the developer to edit the code, and merely reboot the server or game to see the changes; this speeds up iteration time, as the binaries do not need to be recompiled to test minor changes.
A weapon's scripted values are accessed from its GetWpnData()
function in C++.
Standard keys
These are the keyvalues that will be read by CBaseCombatWeapon
. All weapons support them. Script keys are on the left, C++ variables on the right.
Viewmodel and UI
printname / szPrintName
- The friendly name shown to players in the UI; max 80 characters.
- Can use a localization key, as well as a definite string; however, localization keys are case-insensitive, while defined strings are not.
viewmodel / szViewModel
- Path to the viewmodel seen by the player holding the weapon. Relative to the game root, so be sure to include the
models\
folder. Max 80 characters. Not providing a viewmodel and selecting the weapon will cause the game to crash. bucket / iSlot
- The HUD weapon selection in the X axis. 0 being the melee weapons by default.
bucket_position / iPosition
- The HUD weapon selection in the Y axis. 0 Being the pistol in the (1) X axis by default.
weight / iWeight
- Used to determine the weapon's importance when the game auto-selects one.
- The weight used in the name is often erroneously misunderstood to mean weight as a mass, but is actually the weight as a priority (A literary example being, "he's pulling his weight around").
- Higher priority weapons will be selected before others when a weapon runs out of ammo, and a replacement is auto-selected by the game based on this value.
autoswitchto / bAutoSwitchTo
- Whether to switch to this gun on running out of ammo for another. Defaults true.
- Combined with the previous
weight
command can set the order of precedent for auto-selection. - An example of this is the initial appearance of the Magnum in the map
d1_canals_08
in Half-Life 2; when the player walks within pickup range of the .357, the weight value of the Magnum (being the highest in HL2 at 7) combined with this boolean ensures it is automatically selected for the scripted sequence that follows. autoswitchfrom / bAutoSwitchFrom
- Whether to switch away from this weapon when picking up another weapon or ammo. Defaults true.
showusagehint / bShowUsageHint
- Show on-screen hints about how to use the weapon. Defaults false.
BuiltRightHanded / m_bBuiltRightHanded
- Weapon viewmodel is right-handed. Defaults true. Used in Counter-Strike: Source to differentiate weapon models that are modeled as such from the rest.
AllowFlipping / m_bAllowFlipping
- Allow the viewmodel to be mirrored if the user wants left handed weapons. Defaults true.
Worldmodel
playermodel / szWorldModel
- Path to the weapon's regular model that is seen in the world. Relative to the game root, so be sure to include the
\models
folder. Max 80 characters. anim_prefix / szAnimationPrefix
- Prefix of the animations that should be played by characters wielding this weapon (e.g. prefix_idle, prefix_reload). Max 16 characters. Does not appear to have any effect when changed in Half-Life 2, however.
Console/Controller Mode-Specific Variables
Support for some controller features were added with the release of The Orange Box for the Xbox 360, which aimed to take advantage of the force feedback (rumble) system of modern controllers; as well as slightly modified weapon bucket commands, due to the console version using a modified HUD and weapon selection system, via the directional pad (d-pad). To enable this mode, the following console command must be set: hud_fastswitch 2
bucket_360
- The HUD weapon selection on one of the four directional pad buttons. Only supports integers from 0 to 3 (See image).
bucket_position_360
- The HUD weapon selection priority in the four buckets themselves. Weapons shown in the image start at 0, and increment by one each.
rumble / iRumbleEffect
- Which rumble effect to use when fired when playing with a controller. Works on consoles, as well as when using Xbox controllers on PC.
- The table below includes an overview of some of the default
rumble
settings. Of note is that only three rumble effects may be active at any given time (unless changed in the compiled code).
Rumble Settings
rumble -1 / RUMBLE_INVALID
- The fallback if a rumble isn't valid.
rumble 0 / RUMBLE_STOP_ALL
- Cease all current rumbling effects.
rumble 1 / RUMBLE_PISTOL
- Used on the Pistol.
- When used, firing too quickly will prevent the rumble from activating at all.
rumble 2 / RUMBLE_357
- Used on the Magnum.
- Similar to the previous, but firing speed has no ability to stop the rumble from activating.
rumble 3 / RUMBLE_SMG1
- Used on the SMG.
- An initial shake, followed by a rapid fall-off of feedback after a few rounds are fired. Using on a weapon like the Pistol will prevent the rumble from activating if fired too quickly.
rumble 4 / RUMBLE_AR2
- Used on the AR2.
- An initial shake, followed by a slight fall-off of feedback but maintains most of the "punch" of the first shot.
rumble 5 / RUMBLE_SHOTGUN_SINGLE
- Used on the Shotgun for single-shot.
- A fairly large feedback pulse, that consistently maintains the force with every subsequent follow-up shot, even in automatic.
rumble 6 / RUMBLE_SHOTGUN_DOUBLE
- Used on the Shotgun for double-shot.
- Feels identical to the normal shotgun rumble effect.
rumble 7 / RUMBLE_AR2_ALT_FIRE
- Used on the AR2 for the energy ball alt-fire.
- Over the course of the AR2 alt-fire animation, quickly builds a rumble, and then releases at the point of firing the ball.
rumble 8 / RUMBLE_RPG_MISSILE
- Used when firing an RPG rocket.
rumble 9 / RUMBLE_CROWBAR_SWING
- Used when swinging the Crowbar, regardless of if it hit anything.
rumble 10 / RUMBLE_AIRBOAT_GUN
- Used when firing the Airboat gauss cannon.
rumble 11 / RUMBLE_JEEP_ENGINE_LOOP
- Used for the Jeep engine idle.
rumble 12 / RUMBLE_FLAT_LEFT
- Used for the Jeep.
rumble 13 / RUMBLE_FLAT_RIGHT
- Defined, but unused in the Source SDK 2013 code.
rumble 14 / RUMBLE_FLAT_BOTH
- Used for the rumble effects when using the Crane.
rumble 15 / RUMBLE_DMG_LOW
- When taking light damage; defined/unused.
rumble 16 / RUMBLE_DMG_MED
- When taking medium damage; defined/unused.
rumble 17 / RUMBLE_DMG_HIGH
- When taking high damage; defined/unused.
rumble 18 / RUMBLE_FALL_LONG
- When taking a lethal amount of fall damage.
rumble 19 / RUMBLE_FALL_SHORT
- When taking a non-lethal amount of fall damage.
rumble 20 / RUMBLE_PHYSCANNON_OPEN
- When the Gravity Gun claws open.
rumble 21 / RUMBLE_PHYSCANNON_PUNT
- In speculation: when punting something with the Gravity Gun
- In implementation: defined/unused.
rumble 22 / RUMBLE_PHYSCANNON_LOW
- In speculation: when using a low amount of force on an object.
- In implementation: when punting something with the Gravity Gun.
rumble 23 / RUMBLE_PHYSCANNON_MEDIUM
- In speculation: when using a medium amount of force on an object; defined/unused.
rumble 24 / RUMBLE_PHYSCANNON_HIGH
- In speculation: when using a high amount of force on an object; defined/unused.
rumble 25 / RUMBLE_PORTALGUN_LEFT
- When firing the blue portal with the Portal Gun; defined/unused.
rumble 26 / RUMBLE_PORTALGUN_RIGHT
- When firing the orange portal with the Portal Gun; defined/unused.
rumble 27 / RUMBLE_PORTAL_PLACEMENT_FAILURE
- When the Portal Gun fails to place down a portal; defined/unused.
Ammunition
clip_size / iMaxClip1
clip2_size / iMaxClip2
- Magazine size for primary and secondary magazines, respectively.
- Putting -1 for either of these values will disregard the size of the magazine.
default_clip / iDefaultClip1
default_clip2 / iDefaultClip2
- Amount of ammo in the weapon when it spawns. When set to 0, the gun will spawn with no additional ammo, and will be unloaded.
primary_ammo / szAmmo1
secondary_ammo / szAmmo2
- The AmmoDef name of the hitscan ammunition this weapon fires for the primary and secondary/alt-fire modes, respectively. Both default to "None". Max 32 characters each.
MeleeWeapon / m_bMeleeWeapon
- Weapon is a melee-style weapon, such as a crowbar, knife, fists, etc. Defaults false.
An example of the previous from the default weapon_smg1.txt
in HL2:
clip_size 45 default_clip 45 primary_ammo smg1 clip2_size -1 default_clip2 -1 secondary_ammo smg1_grenade
SoundData
The sounddata
subkey defines the sounds that should play when the weapon fires a bullet, fires dry, reloads, and so on. CBaseCombatWeapon
will understand (but not necessarily use) the following:
empty
single_shot
single_shot_npc
double_shot
double_shot_npc
burst
reload
reload_npc
melee_miss
melee_hit
melee_hit_world
special1
special2
special3
taunt
deploy
Weapon_SMG1.Single
or weapon_smg1.single
will return the same result. Keep this in mind when writing sound scripts that case is not solely used to differentiate sounds.An example from the default HL2 weapon_smg1.txt
file:
sounddata { // When the player reloads reload weapon_smg1.reload // When an NPC reloads reload_npc weapon_smg1.npc_reload // Dry-fire empty weapon_smg1.empty // When the player fires single_shot weapon_smg1.single // When an NPC fires single_shot_npc weapon_smg1.npc_single // When a player fire both barrels / alt-fire double_shot weapon_smg1.double // Single-shot fire mode selection; unused special1 weapon_smg1.special1 // Burst fire mode selection; unused special2 weapon_smg1.special2 // Burst fire sound; unused burst weapon_smg1.burst }
TextureData
To define the HUD bucket icons, crosshairs, and ammo icons used by a weapon, you must edit the texturedata
table.
There are primarily two main ways of editing HUD icons: custom fonts, and traditional bitmap images. For more information, see the related article.
Caveats
- Font files require specialized software & knowledge of vector creation
- Bitmap files do not scale with user resolutions
Arguments
Within the texturedata
table, as defined in weapon_resource.cpp
by default there are several values. The values used in the scripts are first, followed by the values as represented in C++ after the slash:
crosshair / iconCrosshair
- Used for the main crosshair that appears on the screen while using this weapon.
autoaim / iconAutoaim
- Used when the crosshair is on a target class, to change the crosshair style and colours.
- Falls back to
crosshair
if not defined. zoom / iconZoomedCrosshair
- Used while zoomed in with the scope on the Crossbow; falls back to
crosshair
if not defined. zoom_autoaim / iconZoomedAutoaim
- Used for the crosshair while zoomed in; falls back to
zoom
if not defined. weapon / iconInactive
- The weapon icon that appears when the HUD is drawn, but the weapon is not selected.
weapon_s / iconActive
- An additional icon that is overlaid on top of the
weapon
icon, to provide a glow effect when the weapon is selected, hence the_s
suffix. This is also reflected in the C++ class names for it and the former. weapon_small / iconSmall
- Used for a variant of the standard weapon font class that is half the size of the original.
ammo / iconAmmo
- An icon used when picking up primary ammo that appears on the side of the screen.
ammo2 / iconAmmo2
- An icon used when picking up secondary/alt-fire ammo that appears on the side of the screen. Examples of this are AR2 balls and SMG grenades.
Defining Glyphs
For each of these definitions, there are two ways to define a glyph for use by default.
The first involves the use of fonts, as defined in the resource/clientscheme.res
file included with every stand-alone Source game. The article linked abouve dictates how to use this.
The second method, is to use bitmap images, such as sprites to define the glyphs.
Font-Specific Method
- font <string>
- The name of a font class defined in the
clientscheme.res
file. In Half-Life 2, they are usually a variation of the nameWeaponIcons
for weapon glyphs. - The font class name entry in
clientsceheme.res
is case-insensitive, and as such, usingWeaponIcons
orweaponicons
will return the same result. Keep this in mind when writing font classes that case is not solely used to differentiate them. - character <string>
- The specific font character to use on the sheet itself. For example, in HL2, on the
WeaponIcons
font,a
is the character used for the SMG. - The character specified is case-sensitive, as using
A
in place of the former changes the glyph to a Lambda symbol used in the Half-Life 2 branding. This allows font sheets to have not just 26 different glyphs across the alphabet, but doubles it for lowercase and uppercase.
Bitmap-Specific Method
- file <path/to/image>
- The path to the image, relative to
materials/
, so the initial root folder name is not needed. - x <integer>
- The lateral (X-axis) position of the desired glyph on the sprite sheet.
- y <integer>
- The longitudinal (Y-axis) position of the desired glyph on the sprite sheet.
- width <integer>
- How wide starting from the
x
of the selected texture to render. - height <integer>
- How tall starting from the
y
of the selected texture to render.
Example
An example from the default scripts/weapon_smg1.txt
found in Half-Life 2:
texturedata { weapon { font weaponicons character a } weapon_s { font weaponiconsselected character a } ammo { font weaponicons character r } ammo2 { font weaponicons character t } crosshair { font crosshairs character Q } autoaim { file sprites/crosshairs x 0 y 48 width 24 height 24 } }
Flags
The following base-2 keyvalue flags are used to imbue the weapon with any combination of the following effects. To add specific flags to a weapon, write each flag you wish to add along with a boolean value next to it, for example:
- ITEM_FLAG_DOHITLOCATIONDMG 1
- ITEM_FLAG_NOAUTORELOAD 1
- ITEM_FLAG_NOAUTOSWITCHEMPTY 1
An alternative method is to instead add up the integers assigned to the flags you wish to use for the weapon and use that value for item_flags.
For example:
item_flags 38
Would give the weapon the ITEM_FLAG_DOHITLOCATIONDMG
, ITEM_FLAG_NOAUTOSWITCHEMPTY
, and ITEM_FLAG_NOAUTORELOAD
flags (32 + 4 + 2, respectively).
1 / ITEM_FLAG_SELECTONEMPTY
- If a player runs out of ammo in their current weapon, and the ITEM_FLAG_NOAUTOSWITCHEMPTY is not assigned to that weapon, select a weapon that has this flag. Note: the 'weight' value assigned to the weapons with this flag affect switch priority.
2 / ITEM_FLAG_NOAUTORELOAD
- Weapon does not automatically reload.
4 / ITEM_FLAG_NOAUTOSWITCHEMPTY
- Weapon does not automatically switch to another weapon when empty.
8 / ITEM_FLAG_LIMITINWORLD
- Weapon is limited in world.
16 / ITEM_FLAG_EXHAUSTIBLE
- A player can totally exhaust their ammo supply and lose this weapon. The Frag Grenade has this flag.
32 / ITEM_FLAG_DOHITLOCATIONDMG
- This weapon takes hit location into account when applying damage.
64 / ITEM_FLAG_NOAMMOPICKUPS
- Don't draw ammo pickup sprites/sounds when ammo is received.
128 / ITEM_FLAG_NOITEMPICKUP
- Don't draw weapon pickup when this weapon is picked up by the player.
Parsing new keys
You will at some point probably want to parse additional keys from a weapon script. You may want to simply because important values like cycle time (how long between each bullet) and bullets per shot are not read from scripts by Valve's code.
Doing it is incredibly easy:
#include "cbase.h"
#include "weapon_parse.h"
#include "KeyValues.h"
class MyWeaponParse : public FileWeaponInfo_t
{
public:
DECLARE_CLASS_GAMEROOT( MyWeaponParse, FileWeaponInfo_t );
void Parse( ::KeyValues* pKeyValuesData, const char* szWeaponName )
{
BaseClass::Parse( pKeyValuesData, szWeaponName );
m_flCycleTime = pKeyValuesData->GetFloat( "CycleTime", 0.15 );
m_iBullets = pKeyValuesData->GetInt( "Bullets", 1 );
}
float m_flCycleTime;
int m_iBullets;
};
// This function probably exists somewhere in your mod already.
FileWeaponInfo_t* CreateWeaponInfo()
{
return new MyWeaponParse;
}
From outside the weapon
To load weapon script keys from outside the weapon class (e.g. for VGUI use):
KeyValues
for other ways to access data from a FileWeaponInfo_t
object.Client:
const FileWeaponInfo_t &weaponInfo = C_BasePlayer::GetLocalPlayer()->GetActiveWeapon()->GetWpnData();
wchar_t* wepname = g_pVGuiLocalize->Find(weaponInfo.szPrintName); // Loading a localised string
Server:
// TODO
Files
Weapons scripts should be located at game\scripts\<classname>.txt
, and everything inside should be a child of a single weapondata
table key.
Encryption
To prevent server operators from changing the behaviour of your weapons, you can ICE encrypt them. This isn't totally secure as your ICE key can be extracted from your mod's binaries by anyone with enough knowledge, but it will head off most tampering.
To implement encryption, choose a secret eight-character alphanumeric key and run your weapon scripts through VICE with it. Then create const unsigned char* GetEncryptionKey()
in your GameRules class and have it return the key.
Once your mod starts loading ICE-encrypted files (.ctx) it will not load normal .txt files, even if a CTX is missing.