mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
924 lines
31 KiB
Lua
924 lines
31 KiB
Lua
--[[
|
|
| This file was obtained through the combined efforts
|
|
| of Madbluntz & Plymouth Antiquarian Society.
|
|
|
|
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
|
| Maloy, DrPepper10 @ RIP, Atle!
|
|
|
|
|
| Visit for more: https://plymouth.thetwilightzone.ru/
|
|
--]]
|
|
|
|
function SWEP:CanPrimaryAttack()
|
|
local owner = self:GetOwner()
|
|
|
|
-- Should we not fire? But first.
|
|
if self:GetBuff_Hook("Hook_ShouldNotFireFirst") then return end
|
|
|
|
-- We're holstering
|
|
if IsValid(self:GetHolster_Entity()) then return end
|
|
if self:GetHolster_Time() > 0 then return end
|
|
|
|
-- Disabled (currently used only by deploy)
|
|
if self:GetState() == ArcCW.STATE_DISABLE then return end
|
|
|
|
-- Coostimzing
|
|
if self:GetState() == ArcCW.STATE_CUSTOMIZE then
|
|
if CLIENT and ArcCW.Inv_Hidden then
|
|
ArcCW.Inv_Hidden = false
|
|
gui.EnableScreenClicker(true)
|
|
elseif game.SinglePlayer() then
|
|
-- Kind of ugly hack: in SP this is only called serverside so we ask client to do the same check
|
|
self:CallOnClient("CanPrimaryAttack")
|
|
end
|
|
return
|
|
end
|
|
|
|
-- A priority animation is playing (reloading, cycling, firemode etc)
|
|
if self:GetPriorityAnim() then return end
|
|
|
|
-- Inoperable, but internally (burst resetting for example)
|
|
if self:GetWeaponOpDelay() > CurTime() then return end
|
|
|
|
-- Safety's on, dipshit
|
|
if self:GetCurrentFiremode().Mode == 0 then
|
|
self:ChangeFiremode(false)
|
|
self:SetNextPrimaryFire(CurTime())
|
|
self.Primary.Automatic = false
|
|
return
|
|
end
|
|
|
|
-- If we are an NPC, do our own little methods
|
|
if owner:IsNPC() then self:NPC_Shoot() return end
|
|
|
|
-- If we are in a UBGL, shoot the UBGL, not the gun
|
|
if self:GetInUBGL() then self:ShootUBGL() return end
|
|
|
|
-- Too early, come back later.
|
|
if self:GetNextPrimaryFire() >= CurTime() then return end
|
|
|
|
-- Gun is locked from heat.
|
|
if self:GetHeatLocked() then return end
|
|
|
|
-- Attempting a bash
|
|
if self:GetState() != ArcCW.STATE_SIGHTS and owner:KeyDown(IN_USE) or self.PrimaryBash then self:Bash() return end
|
|
|
|
-- Throwing weapon
|
|
if self.Throwing then self:PreThrow() return end
|
|
|
|
-- Too close to a wall
|
|
if self:BarrelHitWall() > 0 then return end
|
|
|
|
-- Can't shoot while sprinting
|
|
if self:GetNWState() == ArcCW.STATE_SPRINT and !self:CanShootWhileSprint() then return end
|
|
|
|
-- Maximum burst shots
|
|
if (self:GetBurstCount() or 0) >= self:GetBurstLength() then return end
|
|
|
|
-- We need to cycle
|
|
if self:GetNeedCycle() then return end
|
|
|
|
-- If we have a trigger delay, make sure its progress is done
|
|
if self:GetBuff_Override("Override_TriggerDelay", self.TriggerDelay) and ((!self:GetBuff_Override("Override_TriggerCharge", self.TriggerCharge) and self:GetTriggerDelta() < 1)
|
|
or (self:GetBuff_Override("Override_TriggerCharge", self.TriggerCharge) and self:IsTriggerHeld())) then
|
|
return
|
|
end
|
|
|
|
-- Should we not fire?
|
|
if self:GetBuff_Hook("Hook_ShouldNotFire") then return end
|
|
|
|
-- We made it
|
|
return true
|
|
end
|
|
|
|
function SWEP:TakePrimaryAmmo(num)
|
|
if self:HasBottomlessClip() or self:Clip1() <= 0 then
|
|
if self:Ammo1() <= 0 then return end
|
|
if self:HasInfiniteAmmo() then return end
|
|
self:GetOwner():RemoveAmmo(num, self:GetPrimaryAmmoType())
|
|
return end
|
|
self:SetClip1(self:Clip1() - num)
|
|
end
|
|
|
|
function SWEP:ApplyRandomSpread(dir, spread)
|
|
local radius = math.Rand(0, 1)
|
|
local theta = math.Rand(0, math.rad(360))
|
|
local bulletang = dir:Angle()
|
|
local forward, right, up = bulletang:Forward(), bulletang:Right(), bulletang:Up()
|
|
local x = radius * math.sin(theta)
|
|
local y = radius * math.cos(theta)
|
|
|
|
dir:Set(dir + right * spread * x + up * spread * y)
|
|
end
|
|
|
|
function SWEP:PrimaryAttack()
|
|
local owner = self:GetOwner()
|
|
|
|
self.Primary.Automatic = true
|
|
|
|
if !self:CanPrimaryAttack() then return end
|
|
|
|
local clip = self:Clip1()
|
|
local aps = self:GetBuff("AmmoPerShot")
|
|
|
|
if self:HasBottomlessClip() then
|
|
clip = self:Ammo1()
|
|
if self:HasInfiniteAmmo() then
|
|
clip = math.huge
|
|
end
|
|
end
|
|
|
|
if clip < aps then
|
|
self:SetBurstCount(0)
|
|
self:DryFire()
|
|
|
|
self.Primary.Automatic = false
|
|
|
|
return
|
|
end
|
|
|
|
local dir = (owner:EyeAngles() + self:GetFreeAimOffset()):Forward() --owner:GetAimVector()
|
|
local src = self:GetShootSrc()
|
|
|
|
if bit.band(util.PointContents(src), CONTENTS_WATER) == CONTENTS_WATER and !(self.CanFireUnderwater or self:GetBuff_Override("Override_CanFireUnderwater")) then
|
|
self:DryFire()
|
|
return
|
|
end
|
|
|
|
if self:GetMalfunctionJam() then
|
|
self:DryFire()
|
|
return
|
|
end
|
|
|
|
-- Try malfunctioning
|
|
local mal = self:DoMalfunction(false)
|
|
if mal == true then
|
|
local anim = "fire_jammed"
|
|
self:PlayAnimation(anim, 1, true, 0, true)
|
|
return
|
|
end
|
|
|
|
self:GetBuff_Hook("Hook_PreFireBullets")
|
|
|
|
local desync = ArcCW.ConVars["desync"]:GetBool()
|
|
local desyncnum = (desync and math.random()) or 0
|
|
math.randomseed(math.Round(util.SharedRandom(self:GetBurstCount(), -1337, 1337, !game.SinglePlayer() and self:GetOwner():GetCurrentCommand():CommandNumber() or CurTime()) * (self:EntIndex() % 30241)) + desyncnum)
|
|
|
|
self.Primary.Automatic = true
|
|
|
|
local spread = ArcCW.MOAToAcc * self:GetBuff("AccuracyMOA")
|
|
local disp = self:GetDispersion() * ArcCW.MOAToAcc / 10
|
|
|
|
--dir:Rotate(Angle(0, ArcCW.StrafeTilt(self), 0))
|
|
--dir = dir + VectorRand() * disp
|
|
|
|
self:ApplyRandomSpread(dir, disp)
|
|
|
|
if (CLIENT or game.SinglePlayer()) and ArcCW.ConVars["dev_shootinfo"]:GetInt() >= 3 and disp > 0 then
|
|
local dev_tr = util.TraceLine({
|
|
start = src,
|
|
endpos = src + owner:GetAimVector() * 33000,
|
|
mask = MASK_SHOT,
|
|
filter = {self, self:GetOwner()}
|
|
})
|
|
local dist = (dev_tr.HitPos - src):Length()
|
|
local r = dist / (1 / math.tan(disp)) -- had to google "trig cheat sheet to figure this one out"
|
|
local a = owner:GetAimVector():Angle()
|
|
local r_sqrt = r / math.sqrt(2)
|
|
debugoverlay.Line(dev_tr.HitPos - a:Up() * r, dev_tr.HitPos + a:Up() * r, 5, color_white, true)
|
|
debugoverlay.Line(dev_tr.HitPos - a:Right() * r, dev_tr.HitPos + a:Right() * r, 5, color_white, true)
|
|
debugoverlay.Line(dev_tr.HitPos - a:Right() * r_sqrt - a:Up() * r_sqrt, dev_tr.HitPos + a:Right() * r_sqrt + a:Up() * r_sqrt, 5, color_white, true)
|
|
debugoverlay.Line(dev_tr.HitPos - a:Right() * r_sqrt + a:Up() * r_sqrt, dev_tr.HitPos + a:Right() * r_sqrt - a:Up() * r_sqrt, 5, color_white, true)
|
|
debugoverlay.Text(dev_tr.HitPos, math.Round(self:GetDispersion(), 1) .. "MOA (" .. math.Round(disp, 3) .. "°)", 5)
|
|
end
|
|
|
|
local delay = self:GetFiringDelay()
|
|
|
|
local curtime = CurTime()
|
|
local curatt = self:GetNextPrimaryFire()
|
|
local diff = curtime - curatt
|
|
|
|
if diff > engine.TickInterval() or diff < 0 then
|
|
curatt = curtime
|
|
end
|
|
|
|
self:SetNextPrimaryFire(curatt + delay)
|
|
self:SetNextPrimaryFireSlowdown(curatt + delay) -- shadow for ONLY fire time
|
|
|
|
local num = self:GetBuff("Num")
|
|
|
|
num = num + self:GetBuff_Add("Add_Num")
|
|
|
|
local tracer = self:GetBuff_Override("Override_Tracer", self.Tracer)
|
|
local tracernum = self:GetBuff_Override("Override_TracerNum", self.TracerNum)
|
|
local lastout = self:GetBuff_Override("Override_TracerFinalMag", self.TracerFinalMag)
|
|
if lastout >= clip then
|
|
tracernum = 1
|
|
tracer = self:GetBuff_Override("Override_TracerFinal", self.TracerFinal) or self:GetBuff_Override("Override_Tracer", self.Tracer)
|
|
end
|
|
local dmgtable = self.BodyDamageMults
|
|
dmgtable = self:GetBuff_Override("Override_BodyDamageMults") or dmgtable
|
|
|
|
-- drive by is cool
|
|
src = ArcCW:GetVehicleFireTrace(self:GetOwner(), src, dir) or src
|
|
|
|
local bullet = {}
|
|
bullet.Attacker = owner
|
|
bullet.Dir = dir
|
|
bullet.Src = src
|
|
bullet.Spread = Vector(0, 0, 0) --Vector(spread, spread, spread)
|
|
bullet.Damage = 0
|
|
bullet.Num = num
|
|
|
|
local sglove = math.ceil(num / 3)
|
|
bullet.Force = self:GetBuff("Force", true) or math.Clamp( ( (50 / sglove) / ( (self:GetBuff("Damage") + self:GetBuff("DamageMin")) / (self:GetBuff("Num") * 2) ) ) * sglove, 1, 3 )
|
|
-- Overperforming weapons get the jerf, underperforming gets boost
|
|
bullet.Distance = self:GetBuff("Distance", true) or 33300
|
|
-- Setting AmmoType makes the engine look for the tracer effect on the ammo instead of TracerName!
|
|
--bullet.AmmoType = self.Primary.Ammo
|
|
bullet.HullSize = self:GetBuff("HullSize")
|
|
bullet.Tracer = tracernum or 0
|
|
bullet.TracerName = tracer
|
|
bullet.Weapon = self
|
|
bullet.Callback = function(att, tr, dmg)
|
|
ArcCW:BulletCallback(att, tr, dmg, self)
|
|
end
|
|
|
|
local shootent = self:GetBuff("ShootEntity", true) --self:GetBuff_Override("Override_ShootEntity", self.ShootEntity)
|
|
local shpatt = self:GetBuff_Override("Override_ShotgunSpreadPattern", self.ShotgunSpreadPattern)
|
|
local shpattov = self:GetBuff_Override("Override_ShotgunSpreadPatternOverrun", self.ShotgunSpreadPatternOverrun)
|
|
|
|
local extraspread = AngleRand() * self:GetDispersion() * ArcCW.MOAToAcc / 10
|
|
|
|
local projectiledata = {}
|
|
|
|
if shpatt or shpattov or shootent then
|
|
if shootent then
|
|
projectiledata.ent = shootent
|
|
projectiledata.vel = self:GetBuff("MuzzleVelocity")
|
|
end
|
|
|
|
bullet = self:GetBuff_Hook("Hook_FireBullets", bullet)
|
|
|
|
if !bullet then return end
|
|
|
|
local doent = shootent and num or bullet.Num
|
|
local minnum = shootent and 1 or 0
|
|
|
|
if doent > minnum then
|
|
for n = 1, bullet.Num do
|
|
bullet.Num = 1
|
|
|
|
local dispers = self:GetBuff_Override("Override_ShotgunSpreadDispersion", self.ShotgunSpreadDispersion)
|
|
local offset = self:GetShotgunSpreadOffset(n)
|
|
local calcoff = dispers and (offset * self:GetDispersion() * ArcCW.MOAToAcc / 10) or offset
|
|
|
|
local ang = owner:EyeAngles() + self:GetFreeAimOffset()
|
|
local ang2 = Angle(ang)
|
|
ang2:RotateAroundAxis(ang:Right(), -1 * calcoff.p)
|
|
ang2:RotateAroundAxis(ang:Up(), calcoff.y)
|
|
ang2:RotateAroundAxis(ang:Forward(), calcoff.r)
|
|
|
|
if !self:GetBuff_Override("Override_NoRandSpread", self.NoRandSpread) then -- Needs testing
|
|
ang2 = ang2 + AngleRand() * spread / 5
|
|
end
|
|
|
|
if shootent then
|
|
projectiledata.ang = ang2
|
|
|
|
self:DoPrimaryFire(true, projectiledata)
|
|
else
|
|
bullet.Dir = ang2:Forward()
|
|
|
|
self:DoPrimaryFire(false, bullet)
|
|
end
|
|
end
|
|
elseif shootent then
|
|
local ang = owner:EyeAngles() + self:GetFreeAimOffset()
|
|
|
|
if !self:GetBuff_Override("Override_NoRandSpread", self.NoRandSpread) then
|
|
-- ang = (dir + VectorRand() * spread / 5):Angle()
|
|
|
|
local newdir = Vector(dir)
|
|
self:ApplyRandomSpread(newdir, spread / 5)
|
|
ang = newdir:Angle()
|
|
end
|
|
|
|
projectiledata.ang = ang
|
|
|
|
self:DoPrimaryFire(true, projectiledata)
|
|
end
|
|
else
|
|
if !bullet then return end
|
|
|
|
for n = 1, bullet.Num do
|
|
bullet.Num = 1
|
|
local dirry = Vector(dir.x, dir.y, dir.z)
|
|
math.randomseed(math.Round(util.SharedRandom(n, -1337, 1337, !game.SinglePlayer() and self:GetOwner():GetCurrentCommand():CommandNumber() or CurTime()) * (self:EntIndex() % 30241)) + desyncnum)
|
|
if !self:GetBuff_Override("Override_NoRandSpread", self.NoRandSpread) then
|
|
self:ApplyRandomSpread(dirry, spread)
|
|
bullet.Dir = dirry
|
|
end
|
|
bullet = self:GetBuff_Hook("Hook_FireBullets", bullet) or bullet
|
|
|
|
self:DoPrimaryFire(false, bullet)
|
|
end
|
|
end
|
|
|
|
self:DoRecoil()
|
|
|
|
self:SetNthShot(self:GetNthShot() + 1)
|
|
|
|
owner:DoAnimationEvent(self:GetBuff_Override("Override_AnimShoot") or self.AnimShoot)
|
|
|
|
local shouldsupp = SERVER and !game.SinglePlayer()
|
|
|
|
if shouldsupp then SuppressHostEvents(owner) end
|
|
|
|
self:DoEffects()
|
|
|
|
self:SetBurstCount(self:GetBurstCount() + 1)
|
|
|
|
self:TakePrimaryAmmo(aps)
|
|
|
|
self:DoShootSound()
|
|
self:DoPrimaryAnim()
|
|
|
|
if self:GetCurrentFiremode().Mode < 0 and self:GetBurstCount() == self:GetBurstLength() then
|
|
local postburst = (self:GetCurrentFiremode().PostBurstDelay or 0)
|
|
self:SetWeaponOpDelay(CurTime() + postburst * self:GetBuff_Mult("Mult_PostBurstDelay") + self:GetBuff_Add("Add_PostBurstDelay"))
|
|
end
|
|
|
|
if (self:GetIsManualAction()) and !(self.NoLastCycle and self:Clip1() == 0) then
|
|
local fireanim = self:GetBuff_Hook("Hook_SelectFireAnimation") or self:SelectAnimation("fire")
|
|
local firedelay = self.Animations[fireanim].MinProgress or 0
|
|
self:SetNeedCycle(true)
|
|
self:SetWeaponOpDelay(CurTime() + (firedelay * self:GetBuff_Mult("Mult_CycleTime")))
|
|
self:SetNextPrimaryFire(CurTime() + 0.1)
|
|
end
|
|
|
|
self:ApplyAttachmentShootDamage()
|
|
|
|
self:AddHeat(self:GetBuff("HeatGain"))
|
|
|
|
mal = self:DoMalfunction(true)
|
|
if mal == true then
|
|
local anim = "fire_jammed"
|
|
self:PlayAnimation(anim, 1, true, 0, true)
|
|
end
|
|
|
|
if self:GetCurrentFiremode().Mode == 1 then
|
|
self.LastTriggerTime = -1 -- Cannot fire again until trigger released
|
|
self.LastTriggerDuration = 0
|
|
end
|
|
|
|
self:GetBuff_Hook("Hook_PostFireBullets")
|
|
|
|
if shouldsupp then SuppressHostEvents(nil) end
|
|
end
|
|
|
|
function SWEP:TryBustDoor(ent, dmg)
|
|
ArcCW.TryBustDoor(ent, dmg)
|
|
end
|
|
|
|
function SWEP:DoShootSound(sndoverride, dsndoverride, voloverride, pitchoverride)
|
|
local fsound = self.ShootSound
|
|
local suppressed = self:GetBuff_Override("Silencer")
|
|
|
|
if suppressed then
|
|
fsound = self.ShootSoundSilenced
|
|
end
|
|
|
|
local firstsound = self.FirstShootSound
|
|
|
|
if self:GetBurstCount() == 1 and firstsound then
|
|
fsound = firstsound
|
|
|
|
local firstsil = self.FirstShootSoundSilenced
|
|
|
|
if suppressed then
|
|
fsound = firstsil and firstsil or self.ShootSoundSilenced
|
|
end
|
|
end
|
|
|
|
local lastsound = self.LastShootSound
|
|
|
|
local clip = self:Clip1()
|
|
|
|
if clip == 1 and lastsound then
|
|
fsound = lastsound
|
|
|
|
local lastsil = self.LastShootSoundSilenced
|
|
|
|
if suppressed then
|
|
fsound = lastsil and lastsil or self.ShootSoundSilenced
|
|
end
|
|
end
|
|
|
|
fsound = self:GetBuff_Hook("Hook_GetShootSound", fsound)
|
|
|
|
local distancesound = self.DistantShootSound
|
|
|
|
if suppressed then
|
|
distancesound = self.DistantShootSoundSilenced
|
|
end
|
|
|
|
distancesound = self:GetBuff_Hook("Hook_GetDistantShootSound", distancesound)
|
|
|
|
local spv = self.ShootPitchVariation
|
|
local volume = self.ShootVol
|
|
local pitch = self.ShootPitch * math.Rand(1 - spv, 1 + spv) * self:GetBuff_Mult("Mult_ShootPitch")
|
|
|
|
local v = ArcCW.ConVars["weakensounds"]:GetFloat()
|
|
|
|
volume = volume - v
|
|
|
|
volume = volume * self:GetBuff_Mult("Mult_ShootVol")
|
|
|
|
volume = math.Clamp(volume, 51, 149)
|
|
pitch = math.Clamp(pitch, 0, 255)
|
|
|
|
if sndoverride then fsound = sndoverride end
|
|
if dsndoverride then distancesound = dsndoverride end
|
|
if voloverride then volume = voloverride end
|
|
if pitchoverride then pitch = pitchoverride end
|
|
|
|
if distancesound then self:MyEmitSound(distancesound, 149, pitch, 0.5, CHAN_WEAPON + 1) end
|
|
|
|
if fsound then self:MyEmitSound(fsound, volume, pitch, 1, CHAN_WEAPON) end
|
|
|
|
local data = {
|
|
sound = fsound,
|
|
volume = volume,
|
|
pitch = pitch,
|
|
}
|
|
|
|
self:GetBuff_Hook("Hook_AddShootSound", data)
|
|
end
|
|
|
|
function SWEP:GetMuzzleVelocity()
|
|
local vel = self:GetBuff_Override("Override_PhysBulletMuzzleVelocity", self.PhysBulletMuzzleVelocity)
|
|
|
|
if !vel then
|
|
vel = self:GetBuff("Range") * 3.5
|
|
|
|
if self:GetBuff("DamageMin") > self:GetBuff("Damage") then
|
|
vel = vel * 2
|
|
end
|
|
vel = math.Clamp(vel, 200, 1000)
|
|
end
|
|
|
|
vel = vel / ArcCW.HUToM
|
|
|
|
vel = vel * self:GetBuff_Mult("Mult_PhysBulletMuzzleVelocity")
|
|
|
|
vel = vel * ArcCW.ConVars["bullet_velocity"]:GetFloat()
|
|
|
|
return vel
|
|
end
|
|
|
|
function SWEP:DoPrimaryFire(isent, data)
|
|
local clip = self:Clip1()
|
|
if self:HasBottomlessClip() then
|
|
if !self:GetOwner():IsPlayer() then
|
|
clip = math.huge
|
|
else
|
|
clip = self:Ammo1()
|
|
end
|
|
end
|
|
local owner = self:GetOwner()
|
|
|
|
local shouldphysical = ArcCW.ConVars["bullet_enable"]:GetBool()
|
|
|
|
if self.AlwaysPhysBullet or self:GetBuff_Override("Override_AlwaysPhysBullet") then
|
|
shouldphysical = true
|
|
end
|
|
|
|
if self.NeverPhysBullet or self:GetBuff_Override("Override_NeverPhysBullet") then
|
|
shouldphysical = false
|
|
end
|
|
|
|
if isent then
|
|
self:FireRocket(data.ent, data.vel, data.ang, self.PhysBulletDontInheritPlayerVelocity)
|
|
else
|
|
-- if !game.SinglePlayer() and !IsFirstTimePredicted() then return end
|
|
if !IsFirstTimePredicted() then return end
|
|
|
|
if shouldphysical then
|
|
local tracernum = data.Tracer or 1
|
|
local phystracer = self:GetBuff_Override("Override_PhysTracerProfile", self.PhysTracerProfile)
|
|
local lastout = self:GetBuff_Override("Override_TracerFinalMag", self.TracerFinalMag)
|
|
if lastout >= self:Clip1() then
|
|
phystracer = self:GetBuff_Override("Override_PhysTracerProfileFinal", self.PhysTracerProfileFinal) or phystracer
|
|
elseif tracernum == 0 or clip % tracernum != 0 then
|
|
phystracer = 7
|
|
end
|
|
|
|
local vel = self:GetMuzzleVelocity()
|
|
|
|
vel = vel * data.Dir:GetNormalized()
|
|
|
|
ArcCW:ShootPhysBullet(self, data.Src, vel, phystracer or 0)
|
|
else
|
|
owner:FireBullets(data, true)
|
|
end
|
|
end
|
|
end
|
|
|
|
function SWEP:DoPrimaryAnim()
|
|
local anim = "fire"
|
|
|
|
local inbipod = self:InBipod()
|
|
local iron = self:GetState() == ArcCW.STATE_SIGHTS
|
|
|
|
-- Needs testing
|
|
if inbipod then
|
|
anim = self:SelectAnimation("fire_bipod") or self:SelectAnimation("fire") or anim
|
|
else
|
|
anim = self:SelectAnimation("fire") or anim
|
|
end
|
|
|
|
if (self.ProceduralIronFire and iron) or (self.ProceduralRegularFire and !iron) then anim = nil end
|
|
|
|
anim = self:GetBuff_Hook("Hook_SelectFireAnimation", anim) or anim
|
|
|
|
local time = self:GetBuff_Mult("Mult_FireAnimTime", anim) or 1
|
|
|
|
if anim then self:PlayAnimation(anim, time, true, 0, false) end
|
|
end
|
|
|
|
function SWEP:DoPenetration(tr, penleft, alreadypenned)
|
|
local bullet = {
|
|
Damage = self:GetDamage((tr.HitPos - tr.StartPos):Length() * ArcCW.HUToM),
|
|
DamageType = self:GetBuff_Override("Override_DamageType") or self.DamageType,
|
|
Weapon = self,
|
|
Penetration = self:GetBuff("Penetration"),
|
|
Attacker = self:GetOwner(),
|
|
Travelled = (tr.HitPos - tr.StartPos):Length()
|
|
}
|
|
|
|
ArcCW:DoPenetration(tr, bullet.Damage, bullet, penleft, false, alreadypenned)
|
|
end
|
|
|
|
function SWEP:GetFiringDelay()
|
|
local delay = (self.Delay * (1 / self:GetBuff_Mult("Mult_RPM")))
|
|
delay = self:GetBuff_Hook("Hook_ModifyRPM", delay) or delay
|
|
|
|
return delay
|
|
end
|
|
|
|
function SWEP:GetShootSrc()
|
|
local owner = self:GetOwner()
|
|
|
|
if !IsValid(owner) then return self:GetPos() end
|
|
if owner:IsNPC() then return owner:GetShootPos() end
|
|
|
|
local dir = owner:EyeAngles()
|
|
local offset = Vector(0, 0, 0)
|
|
|
|
if self:GetOwner():Crouching() then
|
|
offset = self:GetBuff_Override("Override_BarrelOffsetCrouch") or self.BarrelOffsetCrouch or offset
|
|
end
|
|
|
|
if self:GetNWState() == ArcCW.STATE_SIGHTS then
|
|
offset = LerpVector(self:GetNWSightDelta(), offset, self:GetBuff_Override("Override_BarrelOffsetSighted", self.BarrelOffsetSighted) or offset)
|
|
else
|
|
offset = LerpVector(1 - self:GetNWSightDelta(), offset, self:GetBuff_Override("Override_BarrelOffsetHip", self.BarrelOffsetHip) or offset)
|
|
end
|
|
|
|
local src = owner:EyePos()
|
|
|
|
|
|
src = src + dir:Right() * offset[1]
|
|
src = src + dir:Forward() * offset[2]
|
|
src = src + dir:Up() * offset[3]
|
|
|
|
return src
|
|
end
|
|
|
|
function SWEP:GetShotgunSpreadOffset(num)
|
|
local rotate = Angle()
|
|
local spreadpt = self:GetBuff_Override("Override_ShotgunSpreadPattern") or self.ShotgunSpreadPattern or {}
|
|
local spreadov = self:GetBuff_Override("Override_ShotgunSpreadPatternOverrun") or self.ShotgunSpreadPatternOverrun or { Angle() }
|
|
|
|
if istable(spreadpt) and istable(spreadov) then
|
|
spreadpt["BaseClass"] = nil
|
|
spreadov["BaseClass"] = nil
|
|
|
|
if num > #spreadpt then
|
|
if spo then
|
|
num = num - #spreadpt
|
|
num = math.fmod(num, #spreadov) + 1
|
|
rotate = spreadov[num]
|
|
else
|
|
num = math.fmod(num, #spreadpt) + 1
|
|
rotate = spreadpt[num]
|
|
end
|
|
else
|
|
rotate = spreadpt[num]
|
|
end
|
|
end
|
|
|
|
local rottoang = {}
|
|
rottoang.num = num
|
|
rottoang.ang = rotate
|
|
|
|
rotate = self:GetBuff_Hook("Hook_ShotgunSpreadOffset", rottoang).ang
|
|
|
|
return rotate or Angle()
|
|
end
|
|
|
|
function SWEP:GetDispersion()
|
|
local owner = self:GetOwner()
|
|
|
|
if vrmod and vrmod.IsPlayerInVR(owner) then return 0 end
|
|
|
|
local hipdisp = self:GetBuff("HipDispersion")
|
|
local sights = self:GetState() == ArcCW.STATE_SIGHTS
|
|
|
|
local hip = hipdisp
|
|
|
|
local sightdisp = self:GetBuff("SightsDispersion")
|
|
if sights then hip = Lerp(self:GetNWSightDelta(), sightdisp, hipdisp) end
|
|
|
|
local speed = owner:GetAbsVelocity():Length()
|
|
local maxspeed = owner:GetWalkSpeed() * self:GetBuff("SpeedMult")
|
|
if sights then maxspeed = maxspeed * self:GetBuff("SightedSpeedMult") end
|
|
speed = math.Clamp(speed / maxspeed, 0, 2)
|
|
|
|
if owner:OnGround() or owner:WaterLevel() > 0 and owner:GetMoveType() != MOVETYPE_NOCLIP then
|
|
hip = hip + speed * self:GetBuff("MoveDispersion")
|
|
elseif owner:GetMoveType() != MOVETYPE_NOCLIP then
|
|
hip = hip + math.max(speed * self:GetBuff("MoveDispersion"), self:GetBuff("JumpDispersion"))
|
|
end
|
|
|
|
if self:InBipod() then hip = hip * (self.BipodDispersion * self:GetBuff_Mult("Mult_BipodDispersion")) end
|
|
|
|
if ArcCW.ConVars["mult_crouchdisp"]:GetFloat() != 1 and owner:OnGround() and owner:Crouching() then
|
|
hip = hip * ArcCW.ConVars["mult_crouchdisp"]:GetFloat()
|
|
end
|
|
|
|
if ArcCW.ConVars["freeaim"]:GetInt() == 1 and !sights then
|
|
hip = hip ^ 0.9
|
|
end
|
|
|
|
--local t = hook.Run("ArcCW_ModDispersion", self, {dispersion = hip})
|
|
--hip = t and t.dispersion or hip
|
|
hip = self:GetBuff_Hook("Hook_ModDispersion", hip) or hip
|
|
|
|
return hip
|
|
end
|
|
|
|
function SWEP:DoShellEject(atti)
|
|
local eff = self:GetBuff_Override("Override_ShellEffect") or self.ShellEffect or "arccw_shelleffect"
|
|
|
|
if eff == "NONE" then return end
|
|
|
|
local owner = self:GetOwner()
|
|
|
|
if !IsValid(owner) then return end
|
|
|
|
local vm = self
|
|
|
|
if !owner:IsNPC() then owner:GetViewModel() end
|
|
|
|
local att = vm:GetAttachment(atti or self:GetBuff_Override("Override_CaseEffectAttachment") or self.CaseEffectAttachment or 2)
|
|
|
|
if !att then return end
|
|
|
|
local pos, ang = att.Pos, att.Ang
|
|
|
|
if pos and ang and self.ShellEjectPosCorrection then
|
|
local up = ang:Up()
|
|
local right = ang:Right()
|
|
local forward = ang:Forward()
|
|
pos = pos + up * self.ShellEjectPosCorrection.z + right * self.ShellEjectPosCorrection.x + forward * self.ShellEjectPosCorrection.y
|
|
end
|
|
|
|
local ed = EffectData()
|
|
ed:SetOrigin(pos)
|
|
ed:SetAngles(ang)
|
|
ed:SetAttachment(atti or self:GetBuff_Override("Override_CaseEffectAttachment") or self.CaseEffectAttachment or 2)
|
|
ed:SetScale(1)
|
|
ed:SetEntity(self)
|
|
ed:SetNormal(ang:Forward())
|
|
ed:SetMagnitude(100)
|
|
|
|
local efov = {}
|
|
efov.eff = eff
|
|
efov.fx = ed
|
|
|
|
if self:GetBuff_Hook("Hook_PreDoEffects", efov) == true then return end
|
|
|
|
util.Effect(eff, ed)
|
|
end
|
|
|
|
function SWEP:DoEffects(att)
|
|
if !game.SinglePlayer() and !IsFirstTimePredicted() then return end
|
|
|
|
local ed = EffectData()
|
|
ed:SetStart(self:GetShootSrc())
|
|
ed:SetOrigin(self:GetShootSrc())
|
|
ed:SetScale(1)
|
|
ed:SetEntity(self)
|
|
ed:SetAttachment(att or self:GetBuff_Override("Override_MuzzleEffectAttachment") or self.MuzzleEffectAttachment or 1)
|
|
|
|
local efov = {}
|
|
efov.eff = "arccw_muzzleeffect"
|
|
efov.fx = ed
|
|
|
|
if self:GetBuff_Hook("Hook_PreDoEffects", efov) == true then return end
|
|
|
|
util.Effect("arccw_muzzleeffect", ed)
|
|
end
|
|
|
|
function SWEP:DryFire()
|
|
|
|
if self.Animations.fire_dry then
|
|
return self:PlayAnimation("fire_dry", 1, true, 0, true)
|
|
end
|
|
self:MyEmitSound(self.ShootDrySound or "weapons/arccw/dryfire.wav", 75, 100, 1, CHAN_ITEM)
|
|
self:SetNextPrimaryFire(CurTime() + 0.25)
|
|
end
|
|
|
|
function SWEP:DoRecoil()
|
|
local single = game.SinglePlayer()
|
|
|
|
if !single and !IsFirstTimePredicted() then return end
|
|
|
|
if single and self:GetOwner():IsValid() and SERVER then self:CallOnClient("DoRecoil") end
|
|
|
|
-- math.randomseed(self:GetBurstLength() + (self.Recoil * 409) + (self.RecoilSide * 519))
|
|
|
|
local rec = {
|
|
Recoil = 1,
|
|
RecoilSide = 1,
|
|
VisualRecoilMul = 1
|
|
}
|
|
rec = self:GetBuff_Hook("Hook_ModifyRecoil", rec) or rec
|
|
|
|
local recoil = rec.Recoil
|
|
local side = rec.RecoilSide
|
|
local visual = rec.VisualRecoilMul
|
|
|
|
local rmul = (recoil or 1) * self:GetBuff_Mult("Mult_Recoil")
|
|
local recv = (visual or 1) * self:GetBuff_Mult("Mult_VisualRecoilMult")
|
|
local recs = (side or 1) * self:GetBuff_Mult("Mult_RecoilSide")
|
|
|
|
-- local rrange = math.Rand(-recs, recs) * self.RecoilSide
|
|
|
|
-- local irec = math.Rand(rrange - 1, rrange + 1)
|
|
-- local recu = math.Rand(0.5, 1)
|
|
|
|
local irec = math.Rand(-1, 1)
|
|
local recu = 1
|
|
|
|
if self:InBipod() then
|
|
local b = self.BipodRecoil * self:GetBuff_Mult("Mult_BipodRecoil")
|
|
|
|
rmul = rmul * b
|
|
recs = recs * b
|
|
recv = recv * b
|
|
end
|
|
|
|
local recoiltbl = self:GetBuff_Override("Override_ShotRecoilTable") or self.ShotRecoilTable
|
|
|
|
if recoiltbl and recoiltbl[self:GetBurstCount()] then rmul = rmul * recoiltbl[self:GetBurstCount()] end
|
|
|
|
if ArcCW.ConVars["mult_crouchrecoil"]:GetFloat() != 1 and self:GetOwner():OnGround() and self:GetOwner():Crouching() then
|
|
rmul = rmul * ArcCW.ConVars["mult_crouchrecoil"]:GetFloat()
|
|
end
|
|
|
|
local punch = Angle()
|
|
|
|
punch = punch + (self:GetBuff_Override("Override_RecoilDirection", self.RecoilDirection) * math.max(self.Recoil, 0.25) * recu * recv * rmul)
|
|
punch = punch + (self:GetBuff_Override("Override_RecoilDirectionSide", self.RecoilDirectionSide) * math.max(self.RecoilSide, 0.25) * irec * recv * rmul)
|
|
punch = punch + Angle(0, 0, 90) * math.Rand(-1, 1) * math.Clamp(self.Recoil, 0.25, 1) * recv * rmul * 0.01
|
|
punch = punch * (self.RecoilPunch or 1) * self:GetBuff_Mult("Mult_RecoilPunch")
|
|
|
|
self:SetFreeAimAngle(self:GetFreeAimAngle() - punch)
|
|
|
|
if CLIENT then self:OurViewPunch(punch) end
|
|
|
|
if CLIENT or single then
|
|
recv = recv * self.VisualRecoilMult
|
|
|
|
self.RecoilAmount = self.RecoilAmount + (self.Recoil * rmul * recu)
|
|
self.RecoilAmountSide = self.RecoilAmountSide + (self.RecoilSide * irec * recs * rmul)
|
|
self.RecoilPunchBack = math.Clamp(self.RecoilAmount * recv * 5, 1, 5)
|
|
|
|
if self.MaxRecoilBlowback > 0 then
|
|
self.RecoilPunchBack = math.Clamp(self.RecoilPunchBack, 0, self.MaxRecoilBlowback)
|
|
end
|
|
|
|
self.RecoilPunchSide = self.RecoilSide * 0.1 * irec * recv * rmul
|
|
self.RecoilPunchUp = self.RecoilRise * 0.1 * recu
|
|
end
|
|
|
|
-- math.randomseed(CurTime() + (self:EntIndex() * 3))
|
|
end
|
|
|
|
function SWEP:GetBurstLength()
|
|
local clip = self:Clip1()
|
|
if self:HasBottomlessClip() then
|
|
clip = self:Ammo1()
|
|
if self:HasInfiniteAmmo() then
|
|
clip = math.huge
|
|
end
|
|
end
|
|
--if clip == 0 then return 1 end
|
|
|
|
local len = self:GetCurrentFiremode().Mode
|
|
|
|
if !len then return self:GetBurstCount() + 10 end
|
|
|
|
local hookedlen = self:GetBuff_Hook("Hook_GetBurstLength", len)
|
|
|
|
if len == 1 then return 1 end
|
|
if len >= 2 then return self:GetBurstCount() + 10 end
|
|
|
|
if hookedlen != len then return hookedlen end
|
|
|
|
if len < 0 then return -len end
|
|
|
|
return self:GetBurstCount() + 10
|
|
end
|
|
|
|
function SWEP:FireAnimationEvent(pos, ang, event, options)
|
|
return true
|
|
end
|
|
|
|
function SWEP:IsRampupWeapon()
|
|
local ovr = self:GetBuff_Override("Override_IsRampupWeapon")
|
|
if ovr != nil then return ovr end
|
|
return self:GetBuff("Damage") < self:GetBuff("DamageMin")
|
|
end
|
|
|
|
function SWEP:GetMinMaxRange()
|
|
local decrease = !self:IsRampupWeapon()
|
|
|
|
local min = self:GetBuff_Override("Override_RangeMin", self.RangeMin or 0)
|
|
local max = self:GetBuff_Override("Override_Range", self.Range)
|
|
local min_add = self:GetBuff_Add("Add_RangeMin")
|
|
local max_add = self:GetBuff_Add("Add_Range")
|
|
local min_mult = self:GetBuff_Mult("Mult_RangeMin")
|
|
local max_mult = self:GetBuff_Mult("Mult_Range")
|
|
|
|
if decrease then
|
|
-- MinRange is also affected by Mult_Range, this is intentional
|
|
local total_min = math.max((min + min_add) * min_mult * max_mult, 0)
|
|
return total_min, math.max((max + max_add) * max_mult, total_min)
|
|
else
|
|
-- For "rampup weapons" (dmgmin > dmg), range buffs *decrease* range, as it ramps up damage quicker
|
|
-- After all, +Range is supposed to be a positive buff no matter the kind of gun
|
|
local total_min = math.max((min - min_add) / min_mult / max_mult, 0)
|
|
return total_min, math.max((max - max_add) / max_mult, total_min)
|
|
end
|
|
end
|
|
|
|
function SWEP:GetRangeFraction(range)
|
|
local min, max = self:GetMinMaxRange()
|
|
if range < min then
|
|
return 0
|
|
else
|
|
return math.Clamp((range - min) / (max - min), 0, 1)
|
|
end
|
|
end
|
|
|
|
function SWEP:GetDamage(range, pellet)
|
|
local ovr = self:GetBuff_Override("Override_Num")
|
|
local add = self:GetBuff_Add("Add_Num")
|
|
local mul = self:GetBuff_Mult("Mult_Num")
|
|
|
|
local num = self.Num
|
|
local nbr = (ovr or num) * mul + add
|
|
local factor = 1
|
|
|
|
-- Total damage should be unchanged regardless of whether the weapon originally fired 1 pellet or > 1
|
|
-- If pellet is set, we return per-pellet damage instead of total damage
|
|
if pellet and num == 1 then
|
|
factor = 1 / ((ovr or 1) * mul + add)
|
|
elseif num != nbr then
|
|
factor = num / nbr
|
|
end
|
|
|
|
--factor = ((pellet and num == 1) and (1 / ((ovr or 1) + add))) or ((num != nbr) and (num / nbr)) or 1
|
|
|
|
if !pellet then factor = factor * nbr end
|
|
|
|
local dmgmax = self:GetBuff("Damage") * factor
|
|
local dmgmin = self:GetBuff("DamageMin") * factor
|
|
local delta = self:GetRangeFraction(range)
|
|
|
|
local lerped = Lerp(delta, dmgmax, dmgmin)
|
|
|
|
return lerped
|
|
end
|
|
|
|
function SWEP:SecondaryAttack()
|
|
return self.Melee2 and self:Bash(true)
|
|
end
|
|
|
|
function SWEP:CanShootWhileSprint()
|
|
return ArcCW.ConVars["mult_shootwhilesprinting"]:GetBool() or self:GetBuff_Override("Override_ShootWhileSprint", self.ShootWhileSprint)
|
|
end
|