Return to Main Page

First Scripted Weapon

Page white text.png Description:Article about SWEP.
link=User:Dim1xs Author:Dim1xs
Calendar.png Created:September 28th 2023

This article will learn you, how to create code your first Scirpted Weapon - SWEP

Scripted Weapons

Better known as a SWep (or SWEP in some cases) a scripted weapon is a weapon that has an underlying script controlling its operation. In this tutorial, we will be making a SWep with fire bullets function.

The Basic Layout

First of all, you need a program, that can open and edit Lua format files.

Go to the directory (folder): '.. /lua/weapons/'

Now create a new folder, with a simple name (without spaces) for your weapon. (for example: weapon_gun)
I recommend naming the folder so that there is a prefix at the beginning: 'weapon_'
I advise you to do this, because the game may have the same prefixes, and the game may break because of this.

Files

In folder that you created, you need to create 3 files:

🔵init.lua

🔵cl_init.lua

🔵shared.lua

init.lua is run Serverside functions.

cl_init.lua is run Clientside functions.

shared.lua is run in both environments (server/clientside).

Now, for perfect work you have to open cl_init.lua and put here basic code:
include( "shared.lua" ) --Must be here, for normal client initialize without errors.

function  SWEP:DrawLargeWeaponBox( bSelected, xpos, ypos, boxWide, boxTall, selectedColor, alpha, number ) --Must be here so that it does not give an error.
end

function  SWEP:DrawModel( flags )
end

function  SWEP:MuzzleFlash( pos1, angles, type, firstPerson )
end
               

Now same for init.lua:
include( "shared.lua" ) --Must be here, for normal server initialize without errors.

function  SWEP:CapabilitiesGet()
end
               

SWEP Functions

You have to know, how classes in lua are working, for simple: if function has self: in the string it means self is the base class of the script.
if you will dont have SWEP: or ENT: etc in your function name, self: will not work! Because SWEP: is like a structure.

Okay so, you must put a basic functions like Name, Viewmodel, Worldmodel of weapon, so for that you can use same functions like in GMOD:
SWEP.Name                  = "Base Weapon" --in SpawnMenu Name
SWEP.PrintName             = "#HL2_357Handgun"
SWEP.ViewModel             = "models/weapons/v_357.mdl"
SWEP.WorldModel            = "models/weapons/w_357.mdl"
SWEP.HoldType              = "python" --dont working
SWEP.Slot                  = 1
SWEP.SlotPos               = 1
SWEP.Weight                = 7
SWEP.item_flags            = 0
SWEP.AutoSwitchTo          = 1
SWEP.AutoSwitchFrom        = 1
SWEP.BuiltRightHanded      = 0
SWEP.AllowFlipping         = 0 --actually does the same like ViewModelFlip in gmod.
SWEP.MeleeWeapon           = 0 --idk what it does.
SWEP.Spawnable             = true
SWEP.AdminOnly             = false
               

Firearm Settings

Each weapon must have its parameters configured (cartridges, magazine stock, initial ammunition, type of cartridges)

Fortunately, in this lua, the ammo def functions work the same way as in gmod, which means that they are tables, and I can show you 2 ways for setting up ammo.

First way, is making in like in default gmod swep:
--This determins how big each clip/magazine for the gun is. You can 
--set it to -1 to disable the ammo system, meaning primary ammo will 
--not be displayed and will not be affected.
SWEP.Primary.ClipSize = 126

--This sets the number of rounds in the clip when you first get the gun. Again it can be set to -1.
SWEP.Primary.DefaultClip = 126

--Obvious. Determines whether the primary fire is automatic. This should be true/false
SWEP.Primary.Automatic = false

--Sets the ammunition type the gun uses, see below for a list of types.
SWEP.Primary.Ammo = "357

SWEP.Secondary.ClipSize = -1
SWEP.Secondary.DefaultClip = -1
SWEP.Secondary.Automatic = false
SWEP.Secondary.Ammo = "None"
               
And here is the second way:
SWEP.Primary = 
{
   ClipSize=126,
   DefaultClip=126,
   Automatic = false,
   Ammo = "357",
}
SWEP.Secondary = 
{
   ClipSize=-1,
   DefaultClip=-1,
   Automatic = false,
   Ammo = "none",
}
               
I like the second way better, it looks clearer to me.

Ammo list:

AR2 - Ammunition of the AR2/Pulse Rifle
AlyxGun - (name in-game "5.7mm Ammo")
Pistol - Ammunition of the 9MM Pistol
SMG1 - Ammunition of the SMG/MP7
357 - Ammunition of the .357 Magnum
XBowBolt - Ammunition of the Crossbow
Buckshot - Ammunition of the Shotgun
RPG_Round - Ammunition of the RPG/Rocket Launcher
SMG1_Grenade - Ammunition for the SMG/MP7 grenade launcher (secondary fire)
            
And the last one is Damage of SWEP:
SWEP.damage             = 15
            

Player Animations

Now, i will show how to add a animations for the player:
SWEP.m_acttable            =
{
   { ACT_RANGE_ATTACK1,       ACT_RANGE_ATTACK_AR2,         true },
   { ACT_RELOAD,             ACT_RELOAD_SMG1,           true },
   { ACT_IDLE,                  ACT_IDLE_SMG1,             true },
   { ACT_IDLE_ANGRY,            ACT_IDLE_ANGRY_SMG1,       true },
   { ACT_WALK,                  ACT_WALK_RifLE,               true },

   { ACT_HL2MP_IDLE,             ACT_HL2MP_IDLE_AR2,              false },
   { ACT_HL2MP_RUN,              ACT_HL2MP_RUN_AR2,               false },
   { ACT_HL2MP_IDLE_CROUCH,         ACT_HL2MP_IDLE_CROUCH_AR2,       false },
   { ACT_HL2MP_WALK_CROUCH,         ACT_HL2MP_WALK_CROUCH_AR2,       false },
   { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2, false },
   { ACT_HL2MP_GESTURE_RELOAD,         ACT_HL2MP_GESTURE_RELOAD_AR2,    false },
   { ACT_HL2MP_JUMP,             ACT_HL2MP_JUMP_AR2,              false },
   { ACT_RANGE_ATTACK1,          ACT_RANGE_ATTACK_AR2,            false },
    { ACT_MP_STAND_IDLE,            ACT_HL2MP_IDLE_AR2,              false },
   { ACT_MP_CROUCH_IDLE,            ACT_HL2MP_IDLE_CROUCH_AR2,       false },

   { ACT_MP_RUN,                 ACT_HL2MP_RUN_AR2,               false },
   { ACT_MP_CROUCHWALK,          ACT_HL2MP_WALK_CROUCH_AR2,       false },

   { ACT_MP_ATTACK_STAND_PRIMARYFIRE,  ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2, false },
   { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2, false },

   { ACT_MP_RELOAD_STAND,           ACT_HL2MP_GESTURE_RELOAD_AR2,    false },
   { ACT_MP_RELOAD_CROUCH,          ACT_HL2MP_GESTURE_RELOAD_AR2,    false },

   { ACT_MP_JUMP,                ACT_HL2MP_JUMP_AR2,              false },
};
               
Sadly HoldType from gmod is still not realised, so we have to use a act table like in C++,
below this is a example with AR2 animations (aka rifle), if you want to change them,
look for ACT Enums on gmod wiki, or wait untill they will be done in this wiki.

Sounds

Sadly, you can't do the shoot sound with the same way like in gmod, and you have to use SWEP.SoundData table:

That's probably same thing like in game weapon scripts (.txt)
SWEP.SoundData          =
{
   empty                = "Weapon_Pistol.Empty",
   single_shot          = "weapons/sound.wav" --any sound here
}
               
Remember! You don't need to write 'sound/' in path! It puts it automatically!

Functions

Now you gonna code!

Starting from basics, SWEP:Initialize is used for defining values/bools and also is used for default weapon functions:
function SWEP:Initialize()
   self.m_bReloadsSingly   = false;
   self.m_bFiresUnderwater = false;
end
               
Below you can see 2 basic functions, they do the same thing that is written on them.

But of course i will give you a example, i'm not sure you could understand anything:
function SWEP:Initialize()
    self.IsBool = true -- it's gonna be true by default
    IsBool = true --the same but not in swep global.
    
    self.RandVal = 5 -- it's gonna equals to 5 by default
    RandVal = 5 -- the same but not in swep global.
end
               
Next we will do the primary attack in our script!
function SWEP:PrimaryAttack()
   --code
end
               
Every time you do the SWEP, don't forget to idenify the entity of the player who uses the weapon!
function SWEP:PrimaryAttack()
   local pPlayer = self:GetOwner() --getting player who uses a weapon.
    
    if ( ToBaseEntity( pPlayer ) == NULL ) then -- returning if player is not valid ( basic check )
      return;
   end

end
               
Then there are primitive functions, animations, sound, subtraction of bullets from the clip:
function SWEP:PrimaryAttack()
   local pPlayer = self:GetOwner() --getting player who uses a weapon.
    
    if ( ToBaseEntity( pPlayer ) == NULL ) then -- returning if player is not valid ( basic check )
      return;
   end
   
    if ( self.m_iClip1 <= 0 ) then --checking  for a empty clip
      if ( not self.m_bFireOnEmpty ) then
         self:Reload();
      else
         self:WeaponSound( 0 );
         self.m_flNextPrimaryAttack = 0.3;
      end

      return;
   end
    
    self:WeaponSound(1) --emit a shoot sound from SoundData
    self:SendWeaponAnim(ACT_VM_PRIMARYATTACK) --do the animation of viewmodel
    pPlayer:SetAnimation( 5 ) --do the animation of player
    self.m_iClip1 = self.m_iClip1 - 1; --remove one ammo from clip
    
    self.m_flNextPrimaryAttack = gpGlobals.curtime() + 0.10; --next primary attack after shoot

end
               
Okay, after that, i gonna show how to do the function for bullet, and recoil:
function SWEP:PrimaryAttack()
   local pPlayer = self:GetOwner() --getting player who uses a weapon.
    
    if ( ToBaseEntity( pPlayer ) == NULL ) then -- returning if player is not valid ( basic check )
      return;
   end
   
    if ( self.m_iClip1 <= 0 ) then --checking  for a empty clip
      if ( not self.m_bFireOnEmpty ) then
         self:Reload();
      else
         self:WeaponSound( 0 );
         self.m_flNextPrimaryAttack = 0.3;
      end

      return;
   end
    
    self:WeaponSound(1) --emit a shoot sound from SoundData
    self:SendWeaponAnim(ACT_VM_PRIMARYATTACK) --do the animation of viewmodel
    pPlayer:SetAnimation( 5 ) --do the animation of player
    self.m_iClip1 = self.m_iClip1 - 1; --remove one ammo from clip
    
    self.m_flNextPrimaryAttack = gpGlobals.curtime() + 0.10; --next primary attack after shoot

   local vecSrc      = pPlayer:Weapon_ShootPosition(); --these two parametrs must be defined!
   local vecAiming      = pPlayer:GetAutoaimVector( 0.08715574274766 );

   -- vecSrc - position of fire, vecAiming - Directory where is shooting, bullet spread, distance, Ammo Type
   local info = FireBulletsInfo_t(1, vecSrc, vecAiming, vec3_origin,4096, self.m_iPrimaryAmmoType) -- actually BulletInfo table
    info.m_pAttacker = pPlayer --defining our attacker
    
    pPlayer:FireBullets( info ) --and shoot the bullet with info from table
    
    pPlayer:ViewPunch( QAngle( -0.5, random.RandomFloat( -1, 1 ), 0 ) ); -- recoil
end
               

Reload

Reload is must be ALWAYS look like that:
function SWEP:Reload()
   ToHL2MPPlayer(self:GetOwner()):DoAnimationEvent( 3 ) -- do the reload animation
   return self:DefaultReload( self:GetMaxClip1(), self:GetMaxClip2(), 182 ); --actually reload our gun
end
               

Thank you for reading this tutorial, see ya later!

JOIN HL2GMED DISCORD SERVER!