--[[ | 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