function SWEP:Reload(force)
    force = force or false

    if self:GetOwner():IsNPC() then
        self:NPC_Reload()
        return
    end

    if self:GetValue("Firemodes") and self:GetOwner():KeyDown(IN_USE) and self:GetOwner():KeyPressed(IN_RELOAD) and self:GetFiremodeAmount() > 1 and !self:GetSafe() then
        self:SwitchFiremode()
        self:EmitSound("tacrp/weapons/pdw/fire_select-1.wav", 75, 100, 1, CHAN_ITEM)
        return
    end

    local stop = self:RunHook("Hook_PreReload")
    if stop then return end

    if !self:GetOwner():KeyPressed(IN_RELOAD) and !force then
        return
    end

    if self:StillWaiting(true) then return end

    if self:GetJammed() then
        local t = self:PlayAnimation("jam", 0.75, true, true)
        self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_CANCEL_RELOAD, t * 1000)
        self:SetJammed(false)
        self:SetCharge(false)
        return
    end

    if !self:CanReloadInSprint() and self:GetIsSprinting() then return end
    if self:GetCapacity() <= 0 then return end
    if self:Clip1() >= self:GetCapacity() then return end
    if self:Ammo1() <= 0 and !self:GetInfiniteAmmo() then return end

    self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)

    local mult = self:GetValue("ReloadTimeMult") / TacRP.ConVars["mult_reloadspeed"]:GetFloat()

    local anim = "reload"

    if self:GetValue("ShotgunReload") then
        anim = "reload_start"
    else
        self:SetTimer(self:GetValue("LoadInTime") * mult, function()
            self:SetLoadedRounds(math.min(self:GetCapacity(), self:Clip1() + self:Ammo1()))
            self:DoBulletBodygroups()
        end, "SetLoadedRounds")
        if self.ReloadUpInTime then
            self:SetTimer(self.ReloadUpInTime * mult, function()
                self:RestoreClip(self:GetCapacity())
                self:SetNthShot(0)
                self:SetEndReload(true)
            end, "ReloadUpIn")
        end
    end

    local t = self:PlayAnimation(anim, mult, true, true)

    self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_RELOAD, t * 1000)

    if SERVER then
        self:SetTimer(self.DropMagazineTime * mult, function()
            self:DropMagazine()
        end, "DropMagazine")
    end

    self:SetLoadedRounds(self:Clip1())

    self:SetReloading(true)
    self:SetEndReload(false)

    self:SetEmptyReload(self:GetValue("ShotgunNoReverseStart") or self:Clip1() == 0)

    self:DoBulletBodygroups()

    self:RunHook("Hook_StartReload")

    self:SetReloadFinishTime(CurTime() + (t * 0.95))
end

function SWEP:DropMagazine()
    -- if !IsFirstTimePredicted() and !game.SinglePlayer() then return end
    if self:GetValue("DropMagazineModel") and TacRP.ConVars["dropmagazinemodel"]:GetBool() then
        local dropamt = math.floor(self:Clip1() / self:GetValue("DropMagazineAmount"))
        local clip1 = self:Clip1()
        for i = 1, self:GetValue("DropMagazineAmount") do
            local mag = ents.Create("TacRP_droppedmag")

            if IsValid(mag) then
                local bone = "ValveBiped.Bip01_R_Hand"
                if i == 2 then bone = "ValveBiped.Bip01_L_Hand" end
                local matrix = self:GetOwner():GetBoneMatrix(self:GetOwner():LookupBone(bone) or -1)
                local pos, ang
                if matrix then
                    pos = matrix:GetTranslation()
                    ang = matrix:GetAngles()
                else
                    pos = self:GetOwner():EyePos() - (self:GetOwner():EyeAngles():Up() * 8)
                    ang = self:GetOwner():EyeAngles()
                end

                mag:SetPos(pos)
                mag:SetAngles(ang)
                mag.Model = self:GetValue("DropMagazineModel")
                mag.ImpactType = self:GetValue("DropMagazineImpact")
                mag:SetOwner(self:GetOwner())
                if clip1 > 0 and TacRP.ConVars["reload_dump"]:GetBool() then
                    local amt = (i == self:GetValue("DropMagazineAmount") and clip1) or dropamt
                    clip1 = clip1 - amt

                    if !self:GetInfiniteAmmo() then
                        mag.AmmoType = self:GetAmmoType()
                        mag.AmmoCount = amt
                    end
                end
                mag:Spawn()

                local phys = mag:GetPhysicsObject()

                if IsValid(phys) then
                    phys:AddAngleVelocity(Vector(math.Rand(-300, 300), math.Rand(-300, 300), math.Rand(-300, 300)))
                end
            end
        end
        self:SetClip1(clip1)
    end
end

function SWEP:RestoreClip(amt)
    local reserve = self:GetInfiniteAmmo() and math.huge or (self:Clip1() + self:Ammo1())

    local lastclip1 = self:Clip1()

    self:SetClip1(math.min(math.min(self:Clip1() + amt, self:GetCapacity()), reserve))

    if !self:GetInfiniteAmmo() then
        reserve = reserve - self:Clip1()
        self:GetOwner():SetAmmo(reserve, self.Primary.Ammo)
    end

    return self:Clip1() - lastclip1
end

function SWEP:Unload(ammotype)
    self:GetOwner():GiveAmmo(self:Clip1(), ammotype or self.Primary.Ammo)
    self:SetClip1(0)
end

function SWEP:EndReload()
    if self:GetValue("ShotgunReload") then
        local mult = self:GetValue("ReloadTimeMult") / TacRP.ConVars["mult_reloadspeed"]:GetFloat()
        if self:Clip1() >= self:GetCapacity() or (!self:GetInfiniteAmmo() and self:Ammo1() == 0) or self:GetEndReload() then

            local cancellable = TacRP.ConVars["reload_sg_cancel"]:GetBool() and !self:GetValue("ShotgunFullCancel")
            local t = 1
            if !self.ShotgunNoReverseStart and (self:Clip1() == self:GetLoadedRounds() or !self:GetEmptyReload()) then
                t = self:PlayAnimation("reload_start", -0.75 * mult, !cancellable, true)
            else
                t = self:PlayAnimation("reload_finish", mult, !cancellable, true)
            end
            self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_RELOAD_END, t * 1000)

            self:SetReloading(false)

            self:SetNthShot(0)

            self:DoBulletBodygroups()

            self:RunHook("Hook_EndReload")
        else
            local t = self:PlayAnimation("reload", mult, true)

            local res = self:GetValue("ShotgunThreeload") and
                    math.min(math.min(3, self:GetCapacity() - self:Clip1()), self:GetInfiniteAmmo() and math.huge or self:Ammo1())
                    or 1

            local delay = self:GetValue("ShotgunUpInTime")
            for i = 1, res do
                self:SetTimer(t * delay * ((i - 1) / 3) + 0.22, function()
                    self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_RELOAD_LOOP, t * 1000)
                    self:RestoreClip(1)
                    self:RunHook("Hook_InsertReload", res)
                end, "ShotgunRestoreClip")
            end

            self:SetTimer(t * self:GetValue("ShotgunLoadInTime") * (res / 3), function()
                self:SetLoadedRounds(self:GetLoadedRounds() + res)
                self:DoBulletBodygroups()
            end, "SetLoadedRounds")

            self:SetReloadFinishTime(CurTime() + (t * delay * (res / 3)) + 0.05)

            self:DoBulletBodygroups()
        end
    else
        if !self.ReloadUpInTime then
            self:RestoreClip(self:GetCapacity())
            self:SetNthShot(0)
        end
        self:SetReloading(false)
        self:SetEndReload(false)

        self:RunHook("Hook_EndReload")
    end
end

function SWEP:CancelReload(doanims, keeptime)
    if self:GetReloading() then

        self:RunHook("Hook_CancelReload")

        local stop = false

        if doanims then
            if self:GetValue("ShotgunReload") then
                local mult = self:GetValue("ReloadTimeMult") / TacRP.ConVars["mult_reloadspeed"]:GetFloat()
                local t = 1
                if self.CurrentAnimation == "reload_start" and self.ShotgunReloadCompleteStart then
                    self:SetEndReload(true)
                elseif self:Clip1() == self:GetLoadedRounds() and !self.ShotgunNoReverseStart then
                    t = self:PlayAnimation("reload_start", -0.75 * mult, true, true)
                    stop = true
                else
                    t = self:PlayAnimation("reload_finish", mult, true, true)
                    stop = true
                end
                self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_RELOAD_END, t * 1000)
            else
                self:Idle()
                stop = true
            end
        else
            stop = true
        end

        if stop then
            self:KillTimer("SetLoadedRounds")
            self:KillTimer("ShotgunRestoreClip")
            self:KillTimer("ReloadUpIn")
            self:KillTimer("DropMagazine")
            self:SetReloading(false)
            self:SetEndReload(false)
            self:SetNthShot(0)
            self:DoBulletBodygroups()
            if !keeptime then
                self:SetReloadFinishTime(0)
            end
        end
    end
end

function SWEP:ThinkReload()
    if self:GetReloading() and self:GetReloadFinishTime() < CurTime() then
        self:EndReload()
    end
end

local launcher_ammo = {
    ["smg1_grenade"] = true,
    ["rpg_round"] = true,
}
function SWEP:GetInfiniteAmmo()
    local ammo = string.lower(self:GetAmmoType())
    if launcher_ammo[ammo] then
        return TacRP.ConVars["infinitelaunchers"]:GetBool() or self:GetValue("InfiniteAmmo")
    else
        return TacRP.ConVars["infiniteammo"]:GetBool() or self:GetValue("InfiniteAmmo")
    end
end

function SWEP:GetCapacity(base)
    if base then
        return self:GetBaseValue("ClipSize")
    else
        return self:GetValue("ClipSize")
    end
end

function SWEP:GetAmmoType(base)
    local valfunc = base and self.GetBaseValue or self.GetValue
    if self.Ammo_Expanded and TacRP.ConVars["expandedammotypes"]:GetBool() then
        return valfunc(self, "Ammo_Expanded") or valfunc(self, "Ammo")
    else
        return valfunc(self, "Ammo")
    end
end

-- Override to disable auto-reload for one reason or another.
function SWEP:ShouldAutoReload()
    return true
end
