function SWEP:GetSwayAmount(pure)
    if self:GetOwner():IsNPC() then return 0 end

    local sway = self:GetValue("Sway")

    local d = self:GetSightDelta() - (self:GetPeeking() and self:GetValue("PeekPenaltyFraction") or 0)
    sway = Lerp(d, sway, self:GetValue("ScopedSway"))

    if self:GetBreath() < 1 then
        sway = sway + (1 * (1 - self:GetBreath()) * (self:GetOutOfBreath() and 1 or 0.5))
    end
    sway = Lerp(self:GetHoldBreathAmount() ^ 0.75, sway, 0)

    if self:GetBlindFire() then
        sway = sway + self:GetValue("BlindFireSway")
    end

    if self:GetOwner():Crouching() and !(self:GetOwner():KeyDown(IN_FORWARD) or self:GetOwner():KeyDown(IN_MOVELEFT) or self:GetOwner():KeyDown(IN_MOVERIGHT) or self:GetOwner():KeyDown(IN_BACK)) then
        sway = sway * self:GetValue("SwayCrouchMult")
    end

    if !pure then
        sway = sway + self:GetForcedSwayAmount()
    end

    if self:GetValue("Bipod") then
        local f = self:Curve(math.Clamp((CurTime() - self.LastBipodTime) / 0.15, 0, 1))
        if self:GetInBipod() then
            sway = Lerp(f, sway, 0)
        else
            sway = Lerp(f, 0, sway)
        end
    end

    return sway
end

function SWEP:GetForcedSwayAmount()
    local sway = 0

    if self:GetOwner():GetNWFloat("TacRPGasEnd", 0) > CurTime() then
        sway = sway + TacRP.ConVars["gas_sway"]:GetFloat() * Lerp(self:GetSightAmount(), 1, 0.25) * math.Clamp((self:GetOwner():GetNWFloat("TacRPGasEnd") - CurTime()) / 2, 0, 1)
    end

    sway = sway + (hook.Run("TacRP_GetForcedSway", self, self:GetOwner()) or 0)

    return sway
end

function SWEP:GetSwayAngles()
    local swayamt = self:IsSwayEnabled() and self:GetSwayAmount() or self:GetForcedSwayAmount()
    local swayspeed = 1

    if swayamt <= 0 then return Angle(0, 0, 0) end

    local ct = CLIENT and UnPredictedCurTime() or CurTime()

    local ang = Angle(math.sin(ct * 0.6 * swayspeed) + (math.cos(ct * 2) * 0.5), math.sin(ct * 0.4 * swayspeed) + (math.cos(ct * 1.6) * 0.5), 0)

    ang = ang * swayamt

    return ang
end

function SWEP:IsSwayEnabled()
    return TacRP.ConVars["sway"]:GetBool()
end

function SWEP:ThinkHoldBreath()
    local owner = self:GetOwner()
    if !owner:IsPlayer() then return end

    local ft = FrameTime()

    if self:HoldingBreath() then
        self:SetBreath(self:GetBreath() - ft * (self:GetBreathDrain() * (self:HasOptic() and 1 or 0.75) * (self:GetRecoilAmount() > 0 and 1.5 or 1)))

        if self:GetBreath() <= 0 then
            self:SetOutOfBreath(true)
            self:SetHoldingBreath(false)
        end

        if self:GetHoldBreathAmount() < 1 then
            self:SetHoldBreathAmount(math.min(1, self:GetHoldBreathAmount() + ft * self:GetBreathSpeed()))
        end
    else
        if self:GetHoldBreathAmount() > 0 then
            self:SetHoldBreathAmount(math.max(0, self:GetHoldBreathAmount() - ft * self:GetBreathSpeed() * 2))
        end

        if self:GetOutOfBreath() and self:GetBreath() >= 1 then
            self:SetOutOfBreath(false)
        end

        self:SetBreath(math.min(1, self:GetBreath() + ft * self:GetValue("BreathRecovery") * (self:GetOutOfBreath() and 0.2 or 0.25)))
    end
end

function SWEP:CanHoldBreath()
    return self:GetValue("Scope") and TacRP.ConVars["sway"]:GetBool() and self:GetScopeLevel() > 0 and !self:GetReloading()
end

function SWEP:NotOutOfBreath()
    return self:GetBreath() > 0 and !self:GetOutOfBreath()
end

function SWEP:HoldingBreath()
    local holding = self:GetOwner():KeyDown(IN_SPEED) or self:GetOwner():KeyDown(IN_RUN)
    if self:GetOwner():GetInfoNum("tacrp_toggleholdbreath", 0) == 1 then
        if self:GetOwner():KeyPressed(IN_SPEED) or self:GetOwner():KeyPressed(IN_RUN) then
            self:SetHoldingBreath(!self:GetHoldingBreath())
        end
    else
        self:SetHoldingBreath(holding)
    end

    lastpressed = holding

    return self:CanHoldBreath() and self:GetSightAmount() >= 1 and self:NotOutOfBreath() and self:GetHoldingBreath()
end

function SWEP:GetBreathDrain()
    if self.MiscCache["breath_cost"] == nil then
        self.MiscCache["breath_cost"] = (math.Clamp(self:GetValue("ScopedSway"), 0.1, 0.3) ^ 0.75) * (1 - 0.3 * math.Clamp((4 - 90 / self:GetValue("ScopeFOV")) / 3, 0, 1))
    end
    return self.MiscCache["breath_cost"] * self:GetValue("BreathDrain")
end

function SWEP:GetBreathSpeed()
    if self.MiscCache["breath_rate"] == nil then
        self.MiscCache["breath_rate"] = (math.Clamp(self:GetValue("ScopedSway"), 0.1, 0.5) ^ 0.5) / 0.3 + (0.5 * math.Clamp((4 - 90 / self:GetValue("ScopeFOV")) / 3, 0, 1))
    end
    return self.MiscCache["breath_rate"]
end
