Your First Scripted Weapon

From GMod Wiki

Jump to: navigation, search
Lua: Your First Scripted Weapon
Page white text.png Description:A step-by-step guide to developing your first Garry's Mod Scripted Weapon.
link=User:Badger Original Author:Badger
Calendar.png Created:14th March 2011


Contents

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. Popular choices for SWeps include launching objects, such as watermelons or chairs, and 'better' versions of existing weapons, like a crossbow that can shoot several bolts per second. In this tutorial, we will be making a SWep with the RPG Launcher model to fire chairs.

Where to start

First of all, it is suggested you download a good code editor such as Notepad++, with a GMod Lua syntax highlighter like this one. This will make your life a lot easier. See Getting Started With Lua for help.

The Basic Layout

Go to the directory (folder): <Steam Folder>/steamapps/<Steam Username>/garrysmod/garrysmod/lua. The default location of the steam folder is C:\Program Files\Steam, or C:\Program Files (x86)\Steam for 64-bit operating systems.

Now, create a new directory with a simple name (without spaces) for your SWep. e.g. chair_thrower

Tip:
Note how the underscore _ has been used to replace spaces.

Files

In a SWep, there are normally 3 script files:

init.lua is run Serverside. This environment will contain functions that will affect the server, such as killing players or throwing things.

cl_init.lua is run Clientside. This is for functions that will affect clients individually. It is used for things like rendering shoot effects/explosions.

shared.lua is run in both environments (server/clientside). Normally this is used only for weapon information.

There is however, an alternative way of laying out SWeps. It is possible to to include both the server-only and client-only code in the shared.lua file. Simply put the serverside/clientside code inside an if statement like so:

 
if SERVER then
    -- Serverside code --
end
 
if CLIENT then
    -- Clientside code --
end
 
Tip:
You can also use the else/elseif statement instead of two if statements, to tidy up the code.

An example of this is shown below:

 
if SERVER then // This is where the init.lua stuff goes.
 
	//This makes sure clients download the file
	AddCSLuaFile ("shared.lua")
 
	//How heavy the SWep is
	SWEP.Weight = 5
 
	//Allow automatic switching to/from this weapon when weapons are picked up
	SWEP.AutoSwitchTo = false
	SWEP.AutoSwitchFrom = false
 
elseif CLIENT then // This is where the cl_init.lua stuff goes
 
	//The name of the SWep, as appears in the weapons tab in the spawn menu(Q Menu)
	SWEP.PrintName = "Chair throwing gun"
 
	//Sets the position of the weapon in the switching menu 
	//(appears when you use the scroll wheel or keys 1-6 by default)
	SWEP.Slot = 4
	SWEP.SlotPos = 1
 
	//Sets drawing the ammuntion levels for this weapon
	SWEP.DrawAmmo = false
 
	//Sets the drawing of the crosshair when this weapon is deployed
	SWEP.DrawCrosshair = false
 
	//Ensures a clean looking notification when a chair is undone. How it works:
	//When you create an undo, you specify the ID:
	//	undo.Create("Some_Identity")
	//By creating an associated language, we can make the undo notification look better:
	//	language.Add("Undone_Some_Identity", "Some message...")
 
	language.Add("Undone_Thrown_SWEP_Entity","Undone Thrown SWEP Entity")
end
 

Note: We will be using the single file (shared.lua) layout for this tutorial.

SWEP Information

At the top of the shared.lua file, you can enter all of the information that will define a SWep's name, category, and certain behaviours, amongst other things. Your SWep will work without most of this information, but it's ideal to include details.

SWEP.Author = "Your Name"
SWEP.Contact = "Your Email Address"
SWEP.Purpose = "What your SWep does."
SWEP.Instructions = "How to operate your SWep"
 
//The category that you SWep will be shown in, in the Spawn (Q) Menu 
//(This can be anything, GMod will create the categories for you)
SWEP.Category = "Category"

The next piece of information is important. It tells Garry's Mod what player groups can see the SWep in the Spawn (Q) Menu.

SWEP.Spawnable = true -- Whether regular players can see it
SWEP.AdminSpawnable = true -- Whether Admins/Super Admins can see it

This information is also important. It defines the SWep's models.

SWEP.ViewModel = "models/weapons/v_RPG.mdl" -- This is the model used for clients to see in first person.
SWEP.WorldModel = "models/weapons/w_rocket_launcher.mdl" -- This is the model shown to all other clients and in third-person.

The following code sets up the SWeps primary/secondary fire and ammo. Primary:

//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 = -1
 
//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 = -1
 
//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 = "none"
 

List of ammo types

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)
SniperRound
SniperPenetratedRound - (name in-game ".45 Ammo")
Grenade - Note you must be given the grenade weapon (weapon_frag) before you can throw grenades.
Thumper - Ammunition cannot exceed 2 (name in-game "Explosive C4 Ammo")
Gravity - (name in-game "4.6MM Ammo")
Battery - (name in-game "9MM Ammo")
GaussEnergy 
CombineCannon - (name in-game ".50 Ammo")
AirboatGun - (name in-game "5.56MM Ammo")
StriderMinigun - (name in-game "7.62MM Ammo")
HelicopterGun
AR2AltFire - Ammunition of the AR2/Pulse Rifle 'combine ball' (secondary fire)
slam - Like Grenade, but for the Selectable Lightweight Attack Munition (S.L.A.M)

The same applies to the secondary system:

SWEP.Secondary.ClipSize = -1
SWEP.Secondary.DefaultClip = -1
SWEP.Secondary.Automatic = false
SWEP.Secondary.Ammo = "none"

Sounds

It is always good practice to precache sounds, so there is no delay when using the gun. It can be done as below:

 
//When the script loads, the sound ''Metal.SawbladeStick'' will be precached, 
//and a local variable with the sound name created.
local ShootSound = Sound("Metal.SawbladeStick")

Functions

This is where we define what the SWep will do...

Unused Functions

Both Reload() and Think() are not needed by this SWep so we will provide empty functions for them.

function SWEP:Reload()
end
 
function SWEP:Think()
end
 

Throw Function

This function we are defining will spawn and throw the chair. It accepts one argument, the filename of the model to be used. That means it can be used to throw almost anything, from barrels to ragdolls.

 
function SWEP:throw_attack (model_file)
	//Get an eye trace. This basically draws an invisible line from
	//the players eye. This SWep makes very little use of the trace, except to 
	//calculate the amount of force to apply to the object thrown.
	local tr = self.Owner:GetEyeTrace()
 
	//Play some noises/effects using the sound we precached earlier
	self:EmitSound(ShootSound)
	self.BaseClass.ShootEffects(self)
 
	//We now exit if this function is not running serverside
	if (!SERVER) then return end
 
	//The next task is to create a physics prop based on the supplied model
	local ent = ents.Create("prop_physics")
	ent:SetModel(model_file)
 
	//Set the initial position and angles of the object. This might need some fine tuning;
	//but it seems to work for the models I have tried.
	ent:SetPos(self.Owner:EyePos() + (self.Owner:GetAimVector() * 16))
	ent:SetAngles(self.Owner:EyeAngles())
	ent:Spawn()
 
	//Now we need to get the physics object for our entity so we can apply a force to it
	local phys = ent:GetPhysicsObject()
 
	//Check if the physics object is valid. If not, remove the entity and stop the function
	if !(phys && IsValid(phys)) then ent:Remove() return end
 
	//Time to apply the force. My method for doing this was almost entirely empirical 
	//and it seems to work fairly intuitively with chairs.
	phys:ApplyForceCenter(self.Owner:GetAimVector():GetNormalized() *  math.pow(tr.HitPos:Length(), 3))
 
	//Now for the important part of adding the spawned objects to the undo and cleanup lists.
	cleanup.Add(self.Owner, "props", ent)
 
	undo.Create ("Thrown_SWEP_Entity")
		undo.AddEntity (ent)
		undo.SetPlayer (self.Owner)
	undo.Finish()
end
 

Attack Functions

Now we've defined the function to spawn and throw props, we need to define the ones that will actually be triggered when we fire:

 
//Throw an office chair on primary attack
function SWEP:PrimaryAttack()
	//Call the throw attack function, with the office chair model
	self:throw_attack("models/props/cs_office/Chair_office.mdl")
end
 
//Throw a wooden chair on secondary attack
function SWEP:SecondaryAttack()
	//Call the throw attack function, this time with the wooden chair model
	self:throw_attack("models/props_c17/FurnitureChair001a.mdl")
end
 

Completed SWep

Once the above is combined, you will have a fully functional chair throwing RPG Launcher. When creating your own SWeps, it's ideal to check and re-check your code. Even the world's best developers make mistakes.

Below is a combination of the code snippets above, Don't just copy the code, make sure you understand it beforehand...

 
if SERVER then // This is where the init.lua stuff goes.
 
	//This makes sure clients download the file
	AddCSLuaFile ("shared.lua")
 
	//How heavy the SWep is
	SWEP.Weight = 5
 
	//Allow automatic switching to/from this weapon when weapons are picked up
	SWEP.AutoSwitchTo = false
	SWEP.AutoSwitchFrom = false
 
elseif CLIENT then // This is where the cl_init.lua stuff goes
 
	//The name of the SWep, as appears in the weapons tab in the spawn menu(Q Menu)
	SWEP.PrintName = "Chair throwing gun"
 
	//Sets the position of the weapon in the switching menu 
	//(appears when you use the scroll wheel or keys 1-6 by default)
	SWEP.Slot = 4
	SWEP.SlotPos = 1
 
	//Sets drawing the ammuntion levels for this weapon
	SWEP.DrawAmmo = false
 
	//Sets the drawing of the crosshair when this weapon is deployed
	SWEP.DrawCrosshair = false
 
	//Ensures a clean looking notification when a chair is undone. How it works:
	//When you create an undo, you specify the ID:
	//		undo.Create("Some_Identity")
	//By creating an associated language, we can make the undo notification look better:
	//		language.Add("Undone_Some_Identity", "Some message...")
 
	language.Add("Undone_Thrown_SWEP_Entity","Undone Thrown SWEP Entity")
end
 
SWEP.Author = "Your Name"
SWEP.Contact = "Your Email Address"
SWEP.Purpose = "What your SWep does."
SWEP.Instructions = "How to operate your SWep"
 
//The category that you SWep will be shown in, in the Spawn (Q) Menu 
//(This can be anything, GMod will create the categories for you)
SWEP.Category = "Category"
 
SWEP.Spawnable = true -- Whether regular players can see it
SWEP.AdminSpawnable = true -- Whether Admins/Super Admins can see it
 
SWEP.ViewModel = "models/weapons/v_RPG.mdl" -- This is the model used for clients to see in first person.
SWEP.WorldModel = "models/weapons/w_rocket_launcher.mdl" -- This is the model shown to all other clients and in third-person.
 
 
//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 = -1
 
//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 = -1
 
//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 = "none"
 
SWEP.Secondary.ClipSize = -1
SWEP.Secondary.DefaultClip = -1
SWEP.Secondary.Automatic = false
SWEP.Secondary.Ammo = "none"
 
//When the script loads, the sound ''Metal.SawbladeStick'' will be precached, 
//and a local variable with the sound name created.
local ShootSound = Sound("Metal.SawbladeStick")
 
function SWEP:Reload()
end
 
function SWEP:Think()
end
 
 
function SWEP:throw_attack (model_file)
	//Get an eye trace. This basically draws an invisible line from
	//the players eye. This SWep makes very little use of the trace, except to 
	//calculate the amount of force to apply to the object thrown.
	local tr = self.Owner:GetEyeTrace()
 
	//Play some noises/effects using the sound we precached earlier
	self:EmitSound(ShootSound)
	self.BaseClass.ShootEffects(self)
 
	//We now exit if this function is not running serverside
	if (!SERVER) then return end
 
	//The next task is to create a physics prop based on the supplied model
	local ent = ents.Create("prop_physics")
	ent:SetModel(model_file)
 
	//Set the initial position and angles of the object. This might need some fine tuning;
	//but it seems to work for the models I have tried.
	ent:SetPos(self.Owner:EyePos() + (self.Owner:GetAimVector() * 16))
	ent:SetAngles(self.Owner:EyeAngles())
	ent:Spawn()
 
	//Now we need to get the physics object for our entity so we can apply a force to it
	local phys = ent:GetPhysicsObject()
 
	//Check if the physics object is valid. If not, remove the entity and stop the function
	if !(phys && IsValid(phys)) then ent:Remove() return end
 
	//Time to apply the force. My method for doing this was almost entirely empirical 
	//and it seems to work fairly intuitively with chairs.
	phys:ApplyForceCenter(self.Owner:GetAimVector():GetNormalized() *  math.pow(tr.HitPos:Length(), 3))
 
	//Now for the important part of adding the spawned objects to the undo and cleanup lists.
	cleanup.Add(self.Owner, "props", ent)
 
	undo.Create ("Thrown_SWEP_Entity")
		undo.AddEntity (ent)
		undo.SetPlayer (self.Owner)
	undo.Finish()
end
 
 
//Throw an office chair on primary attack
function SWEP:PrimaryAttack()
	//Call the throw attack function, with the office chair model
	self:throw_attack("models/props/cs_office/Chair_office.mdl")
end
 
//Throw a wooden chair on secondary attack
function SWEP:SecondaryAttack()
	//Call the throw attack function, this time with the wooden chair model
	self:throw_attack("models/props_c17/FurnitureChair001a.mdl")
end
 


Notes


Credits

Personal tools
Namespaces
Variants
Actions
Navigation
Lua Scripting
Functions
Hooks
Toolbox