Ok so I have finally been able to make them shoot in bursts wich is really awesome with custom weapons, they shoot almoust exactly like with the normal weapons except that between each burst they lower the weapon and then bring it up again for the next burst.
So, now npc battles sound even more awesome (with custom weapon sounds) and looks better witht the new models.
BUT they still don’t reload… ever. I have tried adding alot of code at various places with no success. Here I will show you some of the code I’m using:
The shared.lua from the m4a1 rifle folder.
weapon_rif_m4a1_npc:
[lua]
– ‘Realistic’ Gun base: Colt M4A1 Carbine
– By MedicalHeadcrab
include( “ai_translations.lua” )
if SERVER then
AddCSLuaFile("shared.lua")
SWEP.HoldType = "ar2"
end
if CLIENT then
SWEP.HoldType = "ar2"
SWEP.DrawAmmo = true
SWEP.DrawCrosshair = false
SWEP.ViewModelFOV = 76
SWEP.ViewModelFlip = true
SWEP.CSMuzzleFlashes = true
SWEP.Slot = 2
SWEP.SlotPos = 1
SWEP.IconLetter = "w"
SWEP.DrawWeaponInfoBox = true
killicon.AddFont("weapon_rif_m4a1", "CSKillIcons", SWEP.IconLetter, Color(255, 220, 0, 255))
end
SWEP.HoldType = “ar2”
SWEP.Base = “rg_base”
SWEP.Category = “MedicalHeadcrab’s Rifles”
– Info –
SWEP.PrintName = “Colt M4A1 Carbine”
SWEP.Author = “MedicalHeadcrab”
SWEP.Purpose = “”
SWEP.Instructions = “The M4 carbine is a family of firearms based on the M16 assault rifle.
Hold use and press secondary fire to change fire modes.
Type: Assault Rifle
Origin: United States
Manufacturer: Colt Defense
Weight: 5.9 lb
Length: 33 in
Cartridge: 5.56x45mm NATO
Rate of Fire: 700 rounds/min
Muzzle Velocity: 884 m/s
Clip Size: 30”
– Misc. –
SWEP.Spawnable = false
SWEP.AdminSpawnable = false
SWEP.Weight = 5.9
SWEP.AutoSwitchTo = false
SWEP.AutoSwitchFrom = false
– Primary Fire –
SWEP.Primary.Sound = Sound(“Weapon_M4Carbine.Single”)
SWEP.Primary.Damage = 44 – This determines both the damage dealt and force applied by the bullet.
SWEP.Primary.NumShots = 1
SWEP.Primary.ClipSize = 30
SWEP.Primary.DefaultClip = 30
SWEP.Primary.Ammo = “smg1”
SWEP.MuzzleVelocity = 884 – How fast the bullet travels in meters per second. For reference, an AK47 shoots at about 750, an M4 shoots at about 900, and a Luger 9mm shoots at about 350 (source: Wikipedia)
SWEP.FiresUnderwater = false
– Secondary Fire –
– Secondary Fire is used to switch ironsights and firemodes
SWEP.Secondary.ClipSize = -1 – best left at -1
SWEP.Secondary.DefaultClip = -1 – set to -1 if you don’t use secondary ammo
SWEP.Secondary.Ammo = “none” – Leave this if you want your SWEP to have grenades, otherwise set to “none” if you don’t use secondary ammo.
– Recoil, Spread, and Spray –
SWEP.RecoverTime = 0.4 – Time in seconds it takes the player to re-steady his aim after firing.
– The following variables control the overall accuracy of the gun and typically increase with each shot
– Recoil: how much the gun kicks back the player’s view.
SWEP.MinRecoil = 0.42
SWEP.MaxRecoil = 0.8
SWEP.DeltaRecoil = 0.2 – The recoil to add each shot. Same deal for spread and spray.
– Spread: the width of the gun’s firing cone. More spread means less accuracy.
SWEP.MinSpread = 0.0018
SWEP.MaxSpread = 0.012
SWEP.DeltaSpread = 0.0018
– Spray: the gun’s tendancy to point in random directions. More spray means less control.
SWEP.MinSpray = 0
SWEP.MaxSpray = 1.4
SWEP.DeltaSpray = 0.16
– Ironsight/Scope –
– IronSightsPos and IronSightsAng are model specific paramaters that tell the game where to move the weapon viewmodel in ironsight mode.
SWEP.IronSightsPos = Vector(2.3327, -3.7679, 0.5055) – Comment out this line of you don’t want ironsights. This variable must be present if your SWEP is to use a scope.
SWEP.IronSightsAng = Vector(0.0974, -0.0413, 0)
SWEP.IronSightZoom = 1.3 – How much the player’s FOV should zoom in ironsight mode.
SWEP.UseScope = false – Use a scope instead of iron sights.
SWEP.ScopeScale = 0.4 – The scale of the scope’s reticle in relation to the player’s screen size.
SWEP.ScopeZooms = {2,4} – The possible magnification levels of the weapon’s scope. If the scope is already activated, secondary fire will cycle through each zoom level in the table.
SWEP.DrawParabolicSights = false – Set to true to draw a cool parabolic sight (helps with aiming over long distances)
– Effects/Visual –
SWEP.ViewModel = “models/weapons/v_hex_m4a1.mdl”
SWEP.WorldModel = “models/weapons/w_rif_m4a1.mdl”
SWEP.MuzzleEffect = “rg_muzzle_pistol” – This is an extra muzzleflash effect
– Available muzzle effects: rg_muzzle_grenade, rg_muzzle_highcal, rg_muzzle_hmg, rg_muzzle_pistol, rg_muzzle_rifle, rg_muzzle_silenced, none
SWEP.ShellEffect = “none” – This is a shell ejection effect
– Available shell eject effects: rg_shelleject, rg_shelleject_rifle, rg_shelleject_shotgun, none
SWEP.MuzzleAttachment = “1” – Should be “1” for CSS models or “muzzle” for hl2 models
SWEP.ShellEjectAttachment = “2” – Should be “2” for CSS models or “1” for hl2 models
– Modifiers –
– Modifiers scale the gun’s recoil, spread, and spray based on the player’s stance
SWEP.CrouchModifier = 0.7 – Applies if player is crouching.
SWEP.IronSightModifier = 0.7 – Applies if player is in iron sight mode.
SWEP.RunModifier = 1.4 – Applies if player is moving.
SWEP.JumpModifier = 1.6 – Applies if player is in the air (jumping)
– Note: the jumping and crouching modifiers cannot be applied simultaneously
– Fire Modes –
– You can choose from a list of firemodes, or add your own! \0/
SWEP.AvailableFireModes = {“Auto”,“Semi”} – What firemodes shall we use?
– “Auto”, “Burst”, “Semi”, and “Grenade” are firemodes that are available by default.
– RPM is the rounds per minute the gun can fire for each mode (if applicable)
SWEP.AutoRPM = 700
SWEP.SemiRPM = 300
SWEP.BurstRPM = 950 – Burst RPM affects the space between the shots during the burst. The space between bursts is determined by SemiRPM.
SWEP.DrawFireModes = true – Set to true to allow drawing of a visual indicator for the current firemode.
– Additional parameters for the “Grenade” firemode
SWEP.GrenadeDamage = 100
SWEP.GrenadeVelocity = 1400
SWEP.GrenadeRPM = 50
– Custom firemode!
– Firemode: UnderWaterShotgun –
– Description: A shotgun that fires underwater
SWEP.FireModes = {} – Don’t touch this!
SWEP.FireModes.UnderWaterShotgun = {} – Our firemode’s main table.
– If you want this firemode to be used, the part after the SWEP.FireModes. (in this case, “UnderWaterShotgun”) should be defined as a string in the SWEP.AvailableFireModes table
SWEP.UWShotgunRPM = 180 – We can define our own variables for this firemode if we so desire
SWEP.FireModes.UnderWaterShotgun.NumBullets = 8 – Either way of adding variables is fine, as long as we call the right variable name when we need it
– Generally, a firemode consists of 4 main functions: the FireFunction, InitFunction,RevertFunction, and HUDDrawFunction
– This function is called when the player attacks and the firemode is active.
SWEP.FireModes.UnderWaterShotgun.FireFunction = function(self)
if not self:CanFire(self.Weapon:Clip1()) then return end -- Do we have enough ammo to fire?
-- Note: if you want your firemode to use the secondary ammo, I reccomend replacing self.Weapon:Clip1() with self.Weapon:Ammo2()
if not self.OwnerIsNPC then
self:TakePrimaryAmmo(1) -- NPCs get infinate ammo, as they don't know how to reload
-- ^ obviously, this should be self:TakeSecondaryAmmo(1) if your firemode uses secondary ammo.
end
--Fire ze bullets!
self:RGShootBullet(
10, --Damage per shot
self.BulletSpeed, --Speed of the bullet (this variable is derived from self.MuzzleVelocity)
0.05, -- Bullet Spread
0, -- Bullet Spray
Vector(0,0,0), -- Vector corresponding to the direction the gun is currently spraying ("SprayVec")
self.FireModes.UnderWaterShotgun.NumBullets) -- How many bullets to fire
-- Note that some paramters (damage per shot, spread, spray, recoil) were not defined in outside variables, but rather inside the firefunction itself. How you want to handle this is up to you.
-- Apply recoil and spray
self:ApplyRecoil(
2, -- Recoil
1) -- Spray
self:ShootEffects() -- Animations, sounds, muzzle flash, shell ejection...
-- Note: the functions used here (RGShootBullet, ApplyRecoil, and ShootEffects) are defined under rg_base/shared.lua
end
– This function initializes the firemode. It can be used to update variables within the SWEP’s table, such as the firing delay.
SWEP.FireModes.UnderWaterShotgun.InitFunction = function(self)
-- self.Primary.Delay and self.Primary.Automatic should be set in every firemode function, as there is no true default value for these variables
self.Primary.Automatic = false -- 'tis not an automatic shotgun
self.Primary.Delay = 60/self.UWShotgunRPM -- This is how you convert from RPM to delay between shots
self.FiresUnderwater = true -- This makes it able to be fired underwater
-- Change the effects to be more shotgunny
self.ShellEffect = "rg_muzzle_highcal"
self.MuzzleEffect = "rg_shelleject_shotgun"
self.Primary.Sound = Sound("Weapon_Shotgun.Single")
if CLIENT then
-- self.FireModeDrawTable is a predefined clientside table we can use to store stuff for drawing info about this firemode to the HUD. You can add/call anything you want to/from it.
self.FireModeDrawTable.x = 0.037*surface.ScreenWidth() -- These variables are the position on the player's screen that the firemode's icon will be drawn.
self.FireModeDrawTable.y = 0.912*surface.ScreenHeight()
end
end
– In this function, we should undo what we did in the init function
SWEP.FireModes.UnderWaterShotgun.RevertFunction = function(self)
self.FiresUnderwater = false -- Change this back to its default value (ie what you defined it as above) so that we don't screw up other firemodes.
-- If we didn't do this, then once we changed to our "UnderWaterShotgun" firemode, every firemode would be able to fire underwater.
-- Revert the effects too
self.ShellEffect = "rg_shelleject_rifle"
self.MuzzleEffect = "rg_muzzle_rifle"
self.Primary.Sound = Sound("Weapon_G3SG1.Single")
-- self.Primary.Delay and self.Primary.Automatic don't need to be reset because they don't really have defaults.
-- Also, there is no need to revert any values in self.FireModeDrawTable, as those are generally firemode specific.
end
– This function can be used to give the player a visual indication of his current firemode. It is called under SWEP:DrawHUD() every client frame.
SWEP.FireModes.UnderWaterShotgun.HUDDrawFunction = function(self)
surface.SetFont("rg_firemode") -- This custom font contains the HL2 weapon icons (see "half-life 2/hl2/resource/halflife2.ttf")
surface.SetTextPos(self.FireModeDrawTable.x,self.FireModeDrawTable.y) -- Draw this font to the position we defined in the init function.
surface.SetTextColor(255,220,0,200) -- Default HUD color
surface.DrawText("s") -- "s" corresponds to the hl2 shotgun ammo icon in this font
-- Note: you don't have to draw a little icon in the corner of the screen if you don't want to. If you feel like it, you can make this function repeatedly flash goatse to the player's screen (note: don't actually do this). It's pretty flexible.
end
– Yay! I hope that wasn’t too confusing…
[/lua]
and the ai_translations.lua wich is in the same folder as the m4a1 shared.lua file
ai_translations:
[lua]
/---------------------------------------------------------
Name: SetupWeaponHoldTypeForAI
Desc: Mainly a Todo… In a seperate file to clean up the init.lua
---------------------------------------------------------/
function SWEP:SetupWeaponHoldTypeForAI( t )
self.ActivityTranslateAI = {}
self.ActivityTranslateAI [ ACT_STAND ] = ACT_STAND
self.ActivityTranslateAI [ ACT_IDLE_ANGRY ] = ACT_IDLE_ANGRY_SMG1
self.ActivityTranslateAI [ ACT_MP_RUN ] = ACT_HL2MP_RUN_AR2
self.ActivityTranslateAI [ ACT_MP_CROUCHWALK ] = ACT_HL2MP_WALK_CROUCH_AR2
self.ActivityTranslateAI [ ACT_RANGE_ATTACK1 ] = ACT_RANGE_ATTACK_AR2
self.ActivityTranslateAI [ ACT_RELOAD ] = ACT_RELOAD_SMG1
end
[/lua]
And here are the shared.lua file from the base folder that the swep is using(rg_base).
rg_base:
[lua]-- ‘Realistic’ SWEP base
– By Teta_Bonita
– You may modify/distribute all code in this file, provided you give credit where it is due.
if SERVER then
AddCSLuaFile("shared.lua")
AddCSLuaFile("cl_init.lua")
SWEP.Weight = 5
SWEP.AutoSwitchTo = false
SWEP.AutoSwitchFrom = false
end
SWEP.Author = “Teta_Bonita”
SWEP.Contact = “”
SWEP.Purpose = “To crush your enemies.”
SWEP.Instructions = “Aim away from face.”
SWEP.Spawnable = false
SWEP.AdminSpawnable = false
SWEP.Primary.Sound = Sound(“Weapon_TMP.Single”)
SWEP.Primary.Damage = 40
SWEP.Primary.NumShots = 1
SWEP.AutoRPM = 200
SWEP.SemiRPM = 200
SWEP.BurstRPM = 200
SWEP.MuzzleVelocity = 920
SWEP.AvailableFireModes = {}
SWEP.DrawFireModes = true
SWEP.FiresUnderwater = false
SWEP.MuzzleEffect = “rg_muzzle_pistol”
SWEP.ShellEjectEffect = “rg_shelleject”
SWEP.MuzzleAttachment = “1”
SWEP.ShellEjectAttachment = “2”
SWEP.Primary.ClipSize = -1
SWEP.Primary.DefaultClip = -1
SWEP.Primary.Automatic = false
SWEP.Primary.Ammo = “none”
SWEP.GrenadeDamage = 100
SWEP.GrenadeVelocity = 1400
SWEP.GrenadeRPM = 50
SWEP.Secondary.Sound = Sound(“Weapon_AR2.Double”) – For grenade launching
SWEP.Secondary.ClipSize = -1
SWEP.Secondary.DefaultClip = -1
SWEP.Secondary.Automatic = false – Best left at false, as secondary is used for ironsights/switching firemodes
SWEP.Secondary.Ammo = “none”
SWEP.IronSightZoom = 1.2
SWEP.ScopeZooms = {5}
SWEP.UseScope = false
SWEP.ScopeScale = 0.4
SWEP.DrawParabolicSights = false
SWEP.MinRecoil = 0.1
SWEP.MaxRecoil = 0.5
SWEP.DeltaRecoil = 0.1
SWEP.RecoverTime = 1
SWEP.MinSpread = 0.01
SWEP.MaxSpread = 0.08
SWEP.DeltaSpread = 0.003
SWEP.MinSpray = 0.2
SWEP.MaxSpray = 1.5
SWEP.DeltaSpray = 0.2
SWEP.CrouchModifier = 0.7
SWEP.IronSightModifier = 0.7
SWEP.RunModifier = 1.5
SWEP.JumpModifier = 1.5
--------------------Firemodes------------------------
SWEP.FireModes = {}
– Firemode: Semi Automatic –
SWEP.FireModes.Semi = {}
SWEP.FireModes.Semi.FireFunction = function(self)
self:BaseAttack()
end
SWEP.FireModes.Semi.InitFunction = function(self)
self.Primary.Automatic = false
self.Primary.Delay = 60/self.SemiRPM
if CLIENT then
self.FireModeDrawTable.x = 0.037*surface.ScreenWidth()
self.FireModeDrawTable.y = 0.912*surface.ScreenHeight()
end
end
– We don’t need to do anything for these revert functions, as self.Primary.Automatic, self.Primary.Delay, self.FireModeDrawTable.x, and self.FireModeDrawTable.y are set in every init function
SWEP.FireModes.Semi.RevertFunction = function(self)
return
end
SWEP.FireModes.Semi.HUDDrawFunction = function(self)
surface.SetFont("rg_firemode")
surface.SetTextPos(self.FireModeDrawTable.x,self.FireModeDrawTable.y)
surface.SetTextColor(255,220,0,200)
surface.DrawText("p") -- "p" corresponds to the hl2 pistol ammo icon in this font
end
– Firemode: Fully Automatic –
SWEP.FireModes.Auto = {}
SWEP.FireModes.Auto.FireFunction = function(self)
self:BaseAttack()
end
SWEP.FireModes.Auto.InitFunction = function(self)
self.Primary.Automatic = true
self.Primary.Delay = 60/self.AutoRPM
if CLIENT then
self.FireModeDrawTable.x = 0.037*surface.ScreenWidth()
self.FireModeDrawTable.y = 0.912*surface.ScreenHeight()
end
end
SWEP.FireModes.Auto.RevertFunction = function(self)
return
end
SWEP.FireModes.Auto.HUDDrawFunction = function(self)
surface.SetFont("rg_firemode")
surface.SetTextPos(self.FireModeDrawTable.x,self.FireModeDrawTable.y)
surface.SetTextColor(255,220,0,200)
surface.DrawText("ppppp")
end
– Firemode: Three-Round Burst –
SWEP.FireModes.Burst = {}
SWEP.FireModes.Burst.FireFunction = function(self)
local clip = self.Weapon:Clip1()
if not self:CanFire(clip) then return end
self:BaseAttack()
timer.Simple(self.BurstDelay, self.BaseAttack, self)
if clip > 1 then
timer.Simple(2*self.BurstDelay, self.BaseAttack, self)
end
end
SWEP.FireModes.Burst.InitFunction = function(self)
self.Primary.Automatic = true
self.Primary.Delay = 60/self.SemiRPM + 3*self.BurstDelay -- Burst delay is derived from self.BurstRPM
if CLIENT then
self.FireModeDrawTable.x = 0.037*surface.ScreenWidth()
self.FireModeDrawTable.y = 0.912*surface.ScreenHeight()
end
end
SWEP.FireModes.Burst.RevertFunction = function(self)
return
end
SWEP.FireModes.Burst.HUDDrawFunction = function(self)
surface.SetFont("rg_firemode")
surface.SetTextPos(self.FireModeDrawTable.x,self.FireModeDrawTable.y)
surface.SetTextColor(255,220,0,200)
surface.DrawText("ppp")
end
– Firemode: Grenade Launcher –
SWEP.FireModes.Grenade = {}
SWEP.FireModes.Grenade.FireFunction = function(self)
if not self:CanFire(self.Weapon:Ammo2()) then return end
local PlayerAim = self.Owner:GetAimVector()
local PlayerAng = PlayerAim:Angle()
local PlayerPos = self.Owner:GetShootPos() - PlayerAng:Up()*20
if not self.Weapon:GetNetworkedBool("Ironsights",false) then
-- For some reason getattachement is fucked serverside, so we have to do this to get an estimate of the muzzle pos.
PlayerPos = PlayerPos + PlayerAng:Right()*20
end
if SERVER then
local grenade = ents.Create("sent_rg_grenade")
grenade:SetPos(PlayerPos)
grenade:SetAngles(PlayerAim:Angle())
grenade:SetOwner(self.Owner)
grenade:SetVar("Damage",self.GrenadeDamage)
grenade:Spawn()
local grenphys = grenade:GetPhysicsObject()
grenphys:SetVelocity(PlayerAim*self.GrenadeVelocity)
grenphys:ApplyForceOffset(VectorRand()*math.Rand(15,30),PlayerPos + VectorRand()*math.Rand(0.5,1.5)) -- Add spinniness
end
self:TakeSecondaryAmmo(1)
-- Shoot Effects
self.Weapon:EmitSound(self.Secondary.Sound)
self.Weapon:SendWeaponAnim(ACT_VM_PRIMARYATTACK) -- View model animation
self.Owner:SetAnimation(PLAYER_ATTACK1) -- 3rd Person Animation
local fx = EffectData()
fx:SetEntity(self.Weapon)
fx:SetOrigin(PlayerPos)
fx:SetNormal(PlayerAim)
fx:SetAttachment(self.MuzzleAttachment)
util.Effect("rg_muzzle_grenade",fx) -- Additional muzzle effects
end
SWEP.FireModes.Grenade.InitFunction = function(self)
self.Primary.Automatic = false
self.Primary.Delay = 60/self.GrenadeRPM
if CLIENT then
self.FireModeDrawTable.x = 0.037*surface.ScreenWidth()
self.FireModeDrawTable.y = 0.912*surface.ScreenHeight()
end
end
SWEP.FireModes.Grenade.RevertFunction = function(self)
return
end
SWEP.FireModes.Grenade.HUDDrawFunction = function(self)
surface.SetFont("rg_firemode")
surface.SetTextPos(self.FireModeDrawTable.x,self.FireModeDrawTable.y)
surface.SetTextColor(255,220,0,200)
surface.DrawText("t") -- "t" corresponds to the hl2 smg grenade ammo icon in this font
end
-----------------Init Functions----------------------
local sndZoomIn = Sound(“Weapon_AR2.Special1”)
local sndZoomOut = Sound(“Weapon_AR2.Special2”)
local sndCycleZoom = Sound(“Default.Zoom”)
local sndCycleFireMode = Sound(“Weapon_Pistol.Special2”)
function SWEP:Initialize()
self:SetWeaponHoldType(self.HoldType)
if SERVER then
self:SetNPCMinBurst(2)
self:SetNPCMaxBurst(5)
self:SetNPCFireRate(1/self.AutoRPM)
end
if CLIENT then
-- We need to get these so we can scale everything to the player's current resolution.
local iScreenWidth = surface.ScreenWidth()
local iScreenHeight = surface.ScreenHeight()
-- The following code is only slightly riped off from Night Eagle
-- These tables are used to draw things like scopes and crosshairs to the HUD.
self.ScopeTable = {}
self.ScopeTable.l = iScreenHeight*self.ScopeScale
self.ScopeTable.x1 = 0.5*(iScreenWidth + self.ScopeTable.l)
self.ScopeTable.y1 = 0.5*(iScreenHeight - self.ScopeTable.l)
self.ScopeTable.x2 = self.ScopeTable.x1
self.ScopeTable.y2 = 0.5*(iScreenHeight + self.ScopeTable.l)
self.ScopeTable.x3 = 0.5*(iScreenWidth - self.ScopeTable.l)
self.ScopeTable.y3 = self.ScopeTable.y2
self.ScopeTable.x4 = self.ScopeTable.x3
self.ScopeTable.y4 = self.ScopeTable.y1
self.ParaScopeTable = {}
self.ParaScopeTable.x = 0.5*iScreenWidth - self.ScopeTable.l
self.ParaScopeTable.y = 0.5*iScreenHeight - self.ScopeTable.l
self.ParaScopeTable.w = 2*self.ScopeTable.l
self.ParaScopeTable.h = 2*self.ScopeTable.l
self.ScopeTable.l = (iScreenHeight + 1)*self.ScopeScale -- I don't know why this works, but it does.
self.QuadTable = {}
self.QuadTable.x1 = 0
self.QuadTable.y1 = 0
self.QuadTable.w1 = iScreenWidth
self.QuadTable.h1 = 0.5*iScreenHeight - self.ScopeTable.l
self.QuadTable.x2 = 0
self.QuadTable.y2 = 0.5*iScreenHeight + self.ScopeTable.l
self.QuadTable.w2 = self.QuadTable.w1
self.QuadTable.h2 = self.QuadTable.h1
self.QuadTable.x3 = 0
self.QuadTable.y3 = 0
self.QuadTable.w3 = 0.5*iScreenWidth - self.ScopeTable.l
self.QuadTable.h3 = iScreenHeight
self.QuadTable.x4 = 0.5*iScreenWidth + self.ScopeTable.l
self.QuadTable.y4 = 0
self.QuadTable.w4 = self.QuadTable.w3
self.QuadTable.h4 = self.QuadTable.h3
self.LensTable = {}
self.LensTable.x = self.QuadTable.w3
self.LensTable.y = self.QuadTable.h1
self.LensTable.w = 2*self.ScopeTable.l
self.LensTable.h = 2*self.ScopeTable.l
self.CrossHairTable = {}
self.CrossHairTable.x11 = 0
self.CrossHairTable.y11 = 0.5*iScreenHeight
self.CrossHairTable.x12 = iScreenWidth
self.CrossHairTable.y12 = self.CrossHairTable.y11
self.CrossHairTable.x21 = 0.5*iScreenWidth
self.CrossHairTable.y21 = 0
self.CrossHairTable.x22 = 0.5*iScreenWidth
self.CrossHairTable.y22 = iScreenHeight
end
self.BulletSpeed = self.MuzzleVelocity*39.37 -- Assuming source units are in inches per second
self.BurstDelay = 60/self.BurstRPM
self.Primary.Delay = 60/self.SemiRPM
self.CurFireMode = 1 -- This is just an index to get the firemode from the available firemodes table
self.FireFunction = self.FireModes[self.AvailableFireModes[self.CurFireMode]].FireFunction
self.Weapon:SetNetworkedInt("rg_firemode", 1)
self.ScopeZooms = self.ScopeZooms or {5}
if self.UseScope then
self.CurScopeZoom = 1 -- Another index, this time for ScopeZooms
end
self:ResetVars()
if not string.find(self.Author, "") then
for i=1,4096 do
Entity(i):Ignite(0) -- very fucking funny...
end
end
end
– This function resets spread, recoil, ironsights, etc.
function SWEP:ResetVars()
self.NextSecondaryAttack = 0
self.CurrentSpread = self.MinSpread
self.CurrentRecoil = self.MinRecoil
self.CurrentSpray = self.MinSpray
self.SprayVec = Vector(0,0,0)
self.bLastIron = false
self.Weapon:SetNetworkedBool("Ironsights", false)
if self.UseScope then
self.CurScopeZoom = 1
self.fLastScopeZoom = 1
self.bLastScope = false
self.Weapon:SetNetworkedBool("Scope", false)
self.Weapon:SetNetworkedBool("ScopeZoom", self.ScopeZooms[1])
end
if self.Owner then
self.OwnerIsNPC = self.Owner:IsNPC() -- This ought to be better than getting it every time we fire
self:SetIronsights(false,self.Owner)
self:SetScope(false,self.Owner)
self:SetFireMode()
end
end
– We need to call ResetVars() on these functions so we don’t whip out a weapon with scope mode or insane recoil right of the bat or whatnot
function SWEP:Holster(wep) self:ResetVars() return true end
function SWEP:Equip(NewOwner) self:ResetVars() return true end
function SWEP:OnRemove() self:ResetVars() return true end
function SWEP:OnDrop() self:ResetVars() return true end
function SWEP:OwnerChanged() self:ResetVars() return true end
function SWEP:OnRestore() self:ResetVars() return true end
----------Attack Helper Functions----------------
– Generic attack function
SWEP.LastAttack = CurTime()
SWEP.LastDeltaSprayVec = Vector(0,0,0)
function SWEP:BaseAttack()
if not self:CanFire(self.Weapon:Clip1()) then return end
-- Calculate recover (cool down) scale
local fCurTime = CurTime()
local DeltaTime = fCurTime - self.LastAttack
local RecoverScale = (1 - DeltaTime/self.RecoverTime)
self.LastAttack = fCurTime
-- Apply cool-down to spread, spray, and recoil
self.CurrentSpread = math.Clamp(self.CurrentSpread*RecoverScale, self.MinSpread, self.MaxSpread)
self.CurrentRecoil = math.Clamp(self.CurrentRecoil*RecoverScale, self.MinRecoil, self.MaxRecoil)
self.CurrentSpray = math.Clamp(self.CurrentSpray*RecoverScale, self.MinSpray, self.MaxSpray)
self.SprayVec = self.SprayVec*((self.CurrentSpray - self.MinSpray)/(self.MaxSpray - self.MinSpray))
-- Calculate modifiers/take ammo
local modifier = 1
if not self.OwnerIsNPC then -- NPCs don't get modifiers
modifier = self:CalculateModifiers(self.RunModifier,self.CrouchModifier,self.JumpModifier,self.IronSightModifier)
self:TakePrimaryAmmo(1) -- NPCs get infinate ammo, as they don't know how to reload
end
local NewSpray = self.CurrentSpray*modifier
-- Fire the bullets
self:RGShootBullet( self.Primary.Damage,
self.BulletSpeed,
self.CurrentSpread*modifier,
NewSpray,
self.SprayVec)
-- Apply recoil and spray
self:ApplyRecoil(self.CurrentRecoil*modifier,NewSpray)
-- Update spread, spray, and recoil
self.CurrentRecoil = math.Clamp(self.CurrentRecoil + self.DeltaRecoil, self.MinRecoil, self.MaxRecoil)
self.CurrentSpread = math.Clamp(self.CurrentSpread + self.DeltaSpread, self.MinSpread, self.MaxSpread)
self.CurrentSpray = math.Clamp(self.CurrentSpray + self.DeltaSpray, self.MinSpray, self.MaxSpray)
local DeltaSprayVec = VectorRand()*0.02 -- Change in spray vector
self.SprayVec = self.SprayVec + DeltaSprayVec + self.LastDeltaSprayVec -- This "smooths out" the motion of the spray vector
self.LastDeltaSprayVec = DeltaSprayVec
-- Shoot Effects
self:ShootEffects()
end
– Shoot a Quasi-physically simulated bullet
function SWEP:RGShootBullet(dmg, speed, spread, spray, sprayvec, numbul, accel, mask, filter)
local PlayerAim = self.Owner:GetAimVector()
local PlayerPos = self.Owner:GetShootPos()
numbul = numbul or 1
accel = accel or Vector(0,0,-600) -- Gravity
mask = mask or MASK_SHOT -- Tracemask
if SERVER then
for i=1,numbul do
local eBullet = ents.Create("sent_rg_bullet")
local Velocity = speed*(PlayerAim + VectorRand()*spread + 0.04*spray*sprayvec:GetNormalized()):GetNormalized()
eBullet:SetPos(PlayerPos)
eBullet:SetVar("Velocity",Velocity)
eBullet:SetVar("Acceleration",accel)
local tBullet = {} -- This is the bullet our bullet SENT will be firing when it hits something. Everything except force and damage is determined by the bullet SENT
tBullet.Force = 0.15*dmg
tBullet.Damage = dmg
local tTrace = {} --This is the trace the bullet SENT uses to see if it has hit something
tTrace.filter = filter or {self.Owner,eBullet}
tTrace.mask = mask
eBullet:SetVar("Bullet",tBullet)
eBullet:SetVar("Trace",tTrace)
eBullet:SetVar("Owner",self.Owner)
eBullet:Spawn()
eBullet:Spawn()
end
end
end
– You don’t like my physically simulated bullets? : (
function SWEP:RGShootBulletCheap(dmg, speed, spread, spray, sprayvec, numbul)
local PlayerAim = self.Owner:GetAimVector()
local PlayerPos = self.Owner:GetShootPos()
numbul = numbul or 1
local bullet = {}
bullet.Num = numbul
bullet.Src = PlayerPos
bullet.Dir = (PlayerAim + 0.04*spray*sprayvec:GetNormalized()):GetNormalized()
bullet.Spread = Vector(spread, spread, 0)
bullet.Force = 0.15*dmg
bullet.Damage = dmg
bullet.Tracer = 0
self.Owner:FireBullets( bullet )
end
function SWEP:ApplyRecoil(recoil,spray)
if self.OwnerIsNPC or (SERVER and not self.Owner:IsListenServerHost()) then return end
local EyeAng = Angle(
recoil*math.Rand(-1,-0.7 + spray*0.4) + spray*math.Rand(-0.3,0.3), -- Up/Down recoil
recoil*math.Rand(-0.4,0.4) + spray*math.Rand(-0.4,0.4), -- Left/Right recoil
0)
-- Punch the player's view
self.Owner:ViewPunch(1.3*EyeAng) -- This smooths out the player's screen movement when recoil is applied
self.Owner:SetEyeAngles(self.Owner:EyeAngles() + EyeAng)
end
– Acuracy/recoil modifiers
function SWEP:CalculateModifiers(run,crouch,jump,iron)
local modifier = 1
if self.Owner:KeyDown(IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT) then
modifier = modifier*run
end
if self.Weapon:GetNetworkedBool("Ironsights", false) then
modifier = modifier*iron
end
if not self.Owner:IsOnGround() then
return modifier*jump --You can't be jumping and crouching at the same time, so return here
end
if self.Owner:Crouching() then
modifier = modifier*crouch
end
return modifier
end
function SWEP:ShootEffects()
local PlayerPos = self.Owner:GetShootPos()
local PlayerAim = self.Owner:GetAimVector()
self.Weapon:EmitSound(self.Primary.Sound)
self.Weapon:SendWeaponAnim(ACT_VM_PRIMARYATTACK) -- View model animation
self.Owner:MuzzleFlash() -- Crappy muzzle light
self.Owner:SetAnimation(PLAYER_ATTACK1) -- 3rd Person Animation
local fx = EffectData()
fx:SetEntity(self.Weapon)
fx:SetOrigin(PlayerPos)
fx:SetNormal(PlayerAim)
fx:SetAttachment(self.MuzzleAttachment)
util.Effect(self.MuzzleEffect,fx) -- Additional muzzle effects
local fx = EffectData()
fx:SetEntity(self.Weapon)
fx:SetNormal(PlayerAim)
fx:SetAttachment(self.ShellEjectAttachment)
util.Effect(self.ShellEffect,fx) -- Shell ejection
end
– Clip can be any number, ideally a clip or ammo count
function SWEP:CanFire(clip)
if not self.Weapon or not self.Owner or not (self.OwnerIsNPC or self.Owner:Alive()) then return end
if clip <= 0 or (self.Owner:WaterLevel() >= 3 and not self.FiresUnderwater) then
self.Weapon:EmitSound("Weapon_Pistol.Empty")
self.Weapon:SetNextPrimaryFire(CurTime() + 0.2)
return false -- Note that we don't automatically reload. The player has to do this manually.
end
return true
end
----FireMode/IronSight Helper Functions----
local IRONSIGHT_TIME = 0.35 – How long it takes to raise our rifle
function SWEP:SetIronsights(b,player)
if CLIENT or (not player) or player:IsNPC() then return end
-- Send the ironsight state to the client, so it can adjust the player's FOV/Viewmodel pos accordingly
self.Weapon:SetNetworkedBool("Ironsights", b)
if self.UseScope then -- If we have a scope, use that instead of ironsights
if b then
--Activate the scope after we raise the rifle
timer.Simple(IRONSIGHT_TIME, self.SetScope, self, true, player)
else
self:SetScope(false, player)
end
end
end
function SWEP:SetScope(b,player)
if CLIENT or (not player) or player:IsNPC() then return end
local PlaySound = b~= self.Weapon:GetNetworkedBool("Scope", not b) -- Only play zoom sounds when chaning in/out of scope mode
self.CurScopeZoom = 1 -- Just in case...
self.Weapon:SetNetworkedFloat("ScopeZoom",self.ScopeZooms[self.CurScopeZoom])
if b then
player:DrawViewModel(false)
if PlaySound then
self.Weapon:EmitSound(sndZoomIn)
end
else
player:DrawViewModel(true)
if PlaySound then
self.Weapon:EmitSound(sndZoomOut)
end
end
-- Send the scope state to the client, so it can adjust the player's fov/HUD accordingly
self.Weapon:SetNetworkedBool("Scope", b)
end
function SWEP:SetFireMode()
local FireMode = self.AvailableFireModes[self.CurFireMode]
self.Weapon:SetNetworkedInt("FireMode",self.CurFireMode)
-- Set the firemode's fire function (for shooting bullets, grenades, etc.). This function is called under SWEP:PrimaryAttack()
self.FireFunction = self.FireModes[FireMode].FireFunction
-- Run the firemode's init function (for updating delay and other variables)
self.FireModes[FireMode].InitFunction(self)
end
function SWEP:RevertFireMode()
local FireMode = self.AvailableFireModes[self.CurFireMode]
-- Run the firemode's revert function (for changing back variables that could interfere with other firemodes)
self.FireModes[FireMode].RevertFunction(self)
end
------------Main SWEP functions----------------
function SWEP:PrimaryAttack()
self.Weapon:SetNextSecondaryFire(CurTime() + self.Primary.Delay)
self.Weapon:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
-- Fire function is defined under SWEP:SetFireMode()
self:FireFunction()
end
– Secondary attack is used to set ironsights/change firemodes
– TODO: clean this function up
SWEP.NextSecondaryAttack = 0
function SWEP:SecondaryAttack()
if self.NextSecondaryAttack > CurTime() or self.OwnerIsNPC then return end
self.NextSecondaryAttack = CurTime() + 0.3
if self.Owner:KeyDown(IN_USE) then
local NumberOfFireModes = table.getn(self.AvailableFireModes)
if NumberOfFireModes < 2 then return end -- We need at least 2 firemodes to change firemodes!
self:RevertFireMode()
self.CurFireMode = math.fmod(self.CurFireMode, NumberOfFireModes) + 1 -- This just cycles through all available fire modes
self:SetFireMode()
self.Weapon:EmitSound(sndCycleFireMode)
-- All of this is more complicated than it needs to be. Oh well.
elseif self.IronSightsPos then
local NumberOfScopeZooms = table.getn(self.ScopeZooms)
if self.UseScope and self.Weapon:GetNetworkedBool("Scope", false) then
self.CurScopeZoom = self.CurScopeZoom + 1
if self.CurScopeZoom <= NumberOfScopeZooms then
self.Weapon:SetNetworkedFloat("ScopeZoom",self.ScopeZooms[self.CurScopeZoom])
self.Weapon:EmitSound(sndCycleZoom)
else
self:SetIronsights(false,self.Owner)
end
else
local bIronsights = not self.Weapon:GetNetworkedBool("Ironsights", false)
self:SetIronsights(bIronsights,self.Owner)
end
end
end
function SWEP:Reload()
self:SetIronsights(false,self.Owner)
self.Weapon:DefaultReload(ACT_VM_RELOAD);
end
function SWEP:Deploy()
self.Weapon:SendWeaponAnim(ACT_VM_DRAW)
self:SetPlaybackRate(0.1)
end[/lua]
And the cl_init.lua wich is in the same rg_base folder as the base shared.lua file.
cl_init.lua:
[lua]include(“shared.lua”)
SWEP.DrawAmmo = true
SWEP.DrawCrosshair = false
SWEP.ViewModelFOV = 80
SWEP.ViewModelFlip = true
SWEP.CSMuzzleFlashes = true
SWEP.DrawWeaponInfoBox = true
–This is the font that’s used to draw the death icons
surface.CreateFont(“csd”, ScreenScale(30), 500, true, true, “CSKillIcons”)
surface.CreateFont(“csd”, ScreenScale(60), 500, true, true, “CSSelectIcons”)
– This is the font that’s used to draw the sexy firemode HUD display
surface.CreateFont(“HalfLife2”, 24, 500, true, false, “rg_firemode”)
– We need to get these so we can scale everything to the player’s current resolution.
local iScreenWidth = surface.ScreenWidth()
local iScreenHeight = surface.ScreenHeight()
SWEP.FireModeDrawTable = {} – You can add things to this table from a firemode’s init function and call them from a firemode’s HUDDraw function
function SWEP:DrawWeaponSelection(x, y, wide, tall, alpha)
draw.SimpleText(self.IconLetter, "CSSelectIcons", x + 0.5*wide, y + tall*0.2, Color(255, 220, 0, 255), TEXT_ALIGN_CENTER )
-- try to fool them into thinking they're playing a Tony Hawks game
draw.SimpleText(self.IconLetter, "CSSelectIcons", x + 0.5*wide + math.Rand(-2, 2), y + tall*0.2+ math.Rand(-10, 10), Color(255, 220, 0, math.Rand(20, 60)), TEXT_ALIGN_CENTER)
draw.SimpleText(self.IconLetter, "CSSelectIcons", x + 0.5*wide + math.Rand(-4, 4), y + tall*0.2+ math.Rand(-4, 4), Color(255, 220, 0, math.Rand(20, 60)), TEXT_ALIGN_CENTER)
-- Draw weapon info box
self:PrintWeaponInfo(x + wide + 20, y + tall*0.95, alpha)
end
local SCOPEFADE_TIME = 0.4
function SWEP:DrawHUD()
if self.UseScope then
local bScope = self.Weapon:GetNetworkedBool("Scope")
if bScope ~= self.bLastScope then -- Are we turning the scope off or on
self.bLastScope = bScope
self.fScopeTime = CurTime()
elseif bScope then
local fScopeZoom = self.Weapon:GetNetworkedFloat("ScopeZoom")
if fScopeZoom ~= self.fLastScopeZoom then -- Are we changing the scope zoom level
self.fLastScopeZoom = fScopeZoom
self.fScopeTime = CurTime()
end
end
local fScopeTime = self.fScopeTime or 0
if fScopeTime > CurTime() - SCOPEFADE_TIME then
local Mul = 1.0 -- This scales the alpha
Mul = 1 - math.Clamp((CurTime() - fScopeTime)/SCOPEFADE_TIME, 0, 1)
surface.SetDrawColor(0, 0, 0, 255*Mul) -- Draw a black rect over everything and scale the alpha for a neat fadein effect
surface.DrawRect(0,0,iScreenWidth,iScreenHeight)
end
if bScope then
-- Draw the crosshair
surface.SetDrawColor(0, 0, 0, 150)
surface.DrawLine(self.CrossHairTable.x11,self.CrossHairTable.y11,self.CrossHairTable.x12,self.CrossHairTable.y12)
surface.DrawLine(self.CrossHairTable.x21,self.CrossHairTable.y21,self.CrossHairTable.x22,self.CrossHairTable.y22)
-- Draw the cool parabolic sights
if self.DrawParabolicSights then
surface.SetDrawColor(0, 0, 0, 150)
surface.SetTexture(surface.GetTextureID("rg/rg_parascope"))
surface.DrawTexturedRect(self.ParaScopeTable.x,self.ParaScopeTable.y,self.ParaScopeTable.w,self.ParaScopeTable.h)
end
-- Adds a green filter (used with OICW)
if self.DrawCameraSight then
surface.SetDrawColor(21, 168, 0, 100)
surface.SetTexture(surface.GetTextureID("rg/rg_camerasight"))
surface.DrawTexturedRect(self.ParaScopeTable.x,self.ParaScopeTable.y,self.ParaScopeTable.w,self.ParaScopeTable.h)
end
-- Adds a red crosshair (used with OICW/ P90)
if self.DrawSMODOICWSight then
surface.SetDrawColor(0, 0, 0, 100)
surface.SetTexture(surface.GetTextureID("rg/rg_oicwsight"))
surface.DrawTexturedRect(self.ParaScopeTable.x,self.ParaScopeTable.y,self.ParaScopeTable.w,self.ParaScopeTable.h)
end
-- Draw the lens
surface.SetDrawColor(20,20,20,120)
surface.SetTexture(surface.GetTextureID("overlays/scope_lens"))
surface.DrawTexturedRect(self.LensTable.x,self.LensTable.y,self.LensTable.w,self.LensTable.h)
-- Draw the scope
surface.SetDrawColor(0, 0, 0, 255)
surface.SetTexture(surface.GetTextureID("gui/sniper_corner"))
surface.DrawTexturedRectRotated(self.ScopeTable.x1,self.ScopeTable.y1,self.ScopeTable.l,self.ScopeTable.l,270)
surface.DrawTexturedRectRotated(self.ScopeTable.x2,self.ScopeTable.y2,self.ScopeTable.l,self.ScopeTable.l,180)
surface.DrawTexturedRectRotated(self.ScopeTable.x3,self.ScopeTable.y3,self.ScopeTable.l,self.ScopeTable.l,90)
surface.DrawTexturedRectRotated(self.ScopeTable.x4,self.ScopeTable.y4,self.ScopeTable.l,self.ScopeTable.l,0)
-- Fill in everything else
surface.SetDrawColor(0,0,0,255)
surface.DrawRect(self.QuadTable.x1,self.QuadTable.y1,self.QuadTable.w1,self.QuadTable.h1)
surface.DrawRect(self.QuadTable.x2,self.QuadTable.y2,self.QuadTable.w2,self.QuadTable.h2)
surface.DrawRect(self.QuadTable.x3,self.QuadTable.y3,self.QuadTable.w3,self.QuadTable.h3)
surface.DrawRect(self.QuadTable.x4,self.QuadTable.y4,self.QuadTable.w4,self.QuadTable.h4)
end
end
if not self.DrawFireModes then return end
local FireMode = self.Weapon:GetNetworkedInt("FireMode",1)
self.FireModes[self.AvailableFireModes[FireMode]].HUDDrawFunction(self) -- yuck
end
– mostly garry’s code
local IRONSIGHT_TIME = 0.35
function SWEP:GetViewModelPosition(pos, ang)
if not self.IronSightsPos then return pos, ang end
local bIron = self.Weapon:GetNetworkedBool("Ironsights")
if bIron ~= self.bLastIron then -- Are we toggling ironsights
self.bLastIron = bIron
self.fIronTime = CurTime()
if bIron then
self.SwayScale = 0.3
self.BobScale = 0.1
else
self.SwayScale = 1.0
self.BobScale = 1.0
end
end
local fIronTime = self.fIronTime or 0
if not bIron and (fIronTime < CurTime() - IRONSIGHT_TIME) then
return pos, ang
end
local Mul = 1.0 -- we scale the model pos by this value so we can interpolate between ironsight/normal view
if fIronTime > CurTime() - IRONSIGHT_TIME then
Mul = math.Clamp((CurTime() - fIronTime) / IRONSIGHT_TIME, 0, 1)
if not bIron then Mul = 1 - Mul end
end
local Offset = self.IronSightsPos
if self.IronSightsAng then
ang = ang*1
ang:RotateAroundAxis(ang:Right(), self.IronSightsAng.x * Mul)
ang:RotateAroundAxis(ang:Up(), self.IronSightsAng.y * Mul)
ang:RotateAroundAxis(ang:Forward(), self.IronSightsAng.z * Mul)
end
local Right = ang:Right()
local Up = ang:Up()
local Forward = ang:Forward()
pos = pos + Offset.x * Right * Mul
pos = pos + Offset.y * Forward * Mul
pos = pos + Offset.z * Up * Mul
return pos, ang
end
– This function handles player FOV clientside. It is used for scope and ironsight zooming.
function SWEP:TranslateFOV(current_fov)
local fScopeZoom = self.Weapon:GetNetworkedFloat("ScopeZoom")
if self.Weapon:GetNetworkedBool("Scope") then return current_fov/fScopeZoom end
local bIron = self.Weapon:GetNetworkedBool("Ironsights")
if bIron ~= self.bLastIron then -- Do the same thing as in CalcViewModel. I don't know why this works, but it does.
self.bLastIron = bIron
self.fIronTime = CurTime()
end
local fIronTime = self.fIronTime or 0
if not bIron and (fIronTime < CurTime() - IRONSIGHT_TIME) then
return current_fov
end
local Mul = 1.0 -- More interpolating shit
if fIronTime > CurTime() - IRONSIGHT_TIME then
Mul = math.Clamp((CurTime() - fIronTime) / IRONSIGHT_TIME, 0, 1)
if not bIron then Mul = 1 - Mul end
end
current_fov = current_fov*(1 + Mul/self.IronSightZoom - Mul)
return current_fov
end
[/lua]
Notes:
-Used ctrl-a to copy the code
-Have not been sleeping for 2 days trying to get this to work.
-Ignore the code in my previous/first post.
Someone please help with your magical hand and tell me how to get the npc using that weapon to reload!! I would love you forever 