Your First Scripted Weapon
From GMod Wiki
Lua: Your First Scripted Weapon |
Description: | A step-by-step guide to developing your first Garry's Mod Scripted Weapon. |
Original Author: | Badger |
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:
- shared.lua
- init.lua
- cl_init.lua
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
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
- Page from which world/view model names were retrieved here
- When you make a SWep or code anything, it's a good idea to note down what you want the script to do. It just makes life alot easier and this way you won't get sidetracked.
- A useful list of sounds you can use can be found here. Use sounds that suit the SWep your making, so if you're making a SWep that shoots babies, don't make the shoot sound that of an AR2 or perhaps an engine. Use something like BaseGrenade.BounceSound.
- Last of all, before you start developing/coding anything, have a good idea in your mind of who it will appeal to, how it will work. It is also a good idea to have a Garry's Mod user handy, someone who can test and offer improvements to your script.
Credits
- Badger - creating the original tutorial.
- SgtBurned - extensive improvements throughout early-mid 2011.
- TheFreeman193 - overhauling the tutorial (September 2011)