local function Guns93reloadTypeisOpenBolt(weapon)
    if not weapon or not instanceof(weapon, "HandWeapon") then return false end
    return weapon:getModData()["isOpenBolt"] == "true"
end

local function Guns93reloadTypeisEnBloc(weapon)
    if not weapon or not instanceof(weapon, "HandWeapon") then return false end
    return weapon:getModData()["EnBloc"] == "true"
end

local function Guns93ReloadAnotherStrip(playerObj, gun, bestMag)
    if isClient() then return end
    if bestMag and bestMag:getCurrentAmmoCount() == 0 then
        local highest = 0
        bestMag = nil
        for i = 1, #MagazineGunSet, 10 do
            if MagazineGunSet[i] == gun:getFullType() then
                for j = 1, 9 do
                    local magType = MagazineGunSet[i + j]
                    if magType then
                        local items = playerObj:getInventory():getAllTypeRecurse(magType)
                        for k = 0, items:size() - 1 do
                            local mag = items:get(k)
                            if mag and mag:getCurrentAmmoCount() > highest then
                                if not (mag:hasTag(Gun93Registries.guns93.SpeedLoader) and gun:getCurrentAmmoCount() > 0) then
                                    highest = mag:getCurrentAmmoCount()
                                    bestMag = mag
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    if bestMag and bestMag:getCurrentAmmoCount() > 0 then
        ISInventoryPaneContextMenu.transferIfNeeded(playerObj, bestMag)
        ISTimedActionQueue.add(ISInsertMagazine:new(playerObj, gun, bestMag))
    end
end

local function TriggerEnBlocPing(player, weapon)
    if not SandboxVars.Guns93.Guns93SpentBrass then return end
    if not Guns93reloadTypeisEnBloc(weapon) then return end
    if weapon:getCurrentAmmoCount() ~= 0 or weapon:isRoundChambered() or not weapon:isContainsClip() then return end
    player:playSound('GarandPing')
    local clip = instanceItem(weapon:getMagazineType())
    clip:setCurrentAmmoCount(0)
    local square = player:getCurrentSquare()
    if square then
        local worldClip = square:AddWorldInventoryItem(clip, 0.5, 0.5, 0)
    end
    weapon:setContainsClip(false)
    weapon:setCurrentAmmoCount(0)
    syncHandWeaponFields(player, weapon)
end

local function OnEnBlocCheck()
    local player = getPlayer()
    if not player then return end
    local weapon = player:getPrimaryHandItem()
    if weapon and instanceof(weapon, "HandWeapon") and weapon:isRanged() then
        TriggerEnBlocPing(player, weapon)
    end
end

Events.OnPlayerAttackFinished.Add(OnEnBlocCheck)

local function OnRackKeyPressed(key)
    if key == getCore():getKey("Rack Firearm") then
        local player = getPlayer()
        if not player then return end
        local weapon = player:getPrimaryHandItem()
        if weapon and instanceof(weapon, "HandWeapon") and weapon:isRanged() then
            TriggerEnBlocPing(player, weapon)
        end
    end
end

Events.OnKeyPressed.Add(OnRackKeyPressed)

function DynamicMagClip(mag)
    if not mag or not mag:hasTag(Gun93Registries.guns93.DynamicMagClip) then return end
    local baseIcon = mag:getScriptItem():getIcon()
    local baseSprite = mag:getScriptItem():getWorldStaticModel()
    local suffix = ""
    if mag:getCurrentAmmoCount() < 1 then
        suffix = ""
    elseif mag:getCurrentAmmoCount() < mag:getMaxAmmo() then
        suffix = "_Half"
    elseif mag:getCurrentAmmoCount() >= mag:getMaxAmmo() then
        suffix = "_Full"
    end
    local newIcon = baseIcon .. suffix
    local newSprite = baseSprite .. suffix
    mag:setTexture(getTexture("media/textures/item_" .. newIcon .. ".png"))
    mag:setWorldStaticModel(newSprite)
end

local ISLoadBulletsInMagazine_animEvent_Old = ISLoadBulletsInMagazine.animEvent
function ISLoadBulletsInMagazine:animEvent(event, parameter)
    if event == 'InsertBullet' then
		if self:isLoadFinished() then
			return
		end
        if self:isLocal() then
            if self.loadedThisLoop then
                return
            end
        end
        self.loadedThisLoop = true
		if not isClient() then
			local chance = 5;
			local xp = 1;
			if self.character:getPerkLevel(Perks.Reloading) < 5 then
				chance = 2;
				xp = 4;
			end
			if ZombRand(chance) == 0 then
				addXp(self.character, Perks.Reloading, xp)
			end
            local itemKey = self.magazine:getAmmoType():getItemKey();
			local removedBullet = self.character:getInventory():RemoveOneOf(itemKey, true)
            if removedBullet then
                self.magazine:setCurrentAmmoCount(self.magazine:getCurrentAmmoCount() + 1)
                DynamicMagClip(self.magazine)
                sendRemoveItemFromContainer(self.character:getInventory(), removedBullet)
                syncItemFields(self.character, self.magazine)
                syncItemModData(self.character, self.magazine)
            end
            self.character:getInventory():setDirty(true)
        end
    else
        return ISLoadBulletsInMagazine_animEvent_Old(self, event, parameter)
    end
end

local ISUnloadBulletsFromMagazine_animEvent_Old = ISUnloadBulletsFromMagazine.animEvent
function ISUnloadBulletsFromMagazine:animEvent(event, parameter)
    if event == 'RemoveBullet' then
		if self.magazine:getCurrentAmmoCount() <= 0 then
			return
		end
        if self:isLocal() then
            if self.unloadedThisLoop then
                return
            end
        end
        self.unloadedThisLoop = true
		if not isClient() then
		    local itemKey = self.magazine:getAmmoType():getItemKey();
			local newBullet = instanceItem(itemKey);
			self.character:getInventory():AddItem(newBullet)
			self.magazine:setCurrentAmmoCount(self.magazine:getCurrentAmmoCount() - 1)
            DynamicMagClip(self.magazine)
			sendAddItemToContainer(self.character:getInventory(), newBullet)
			syncItemFields(self.character, self.magazine)
            syncItemModData(self.character, self.magazine)
		end
		self.unloadFinished = false
    else
        return ISUnloadBulletsFromMagazine_animEvent_Old(self, event, parameter)
    end
end

local ISInsertMagazine_loadAmmo_Old = ISInsertMagazine.loadAmmo
function ISInsertMagazine:loadAmmo()
    if isClient() then return end
    if not self.magazine:hasTag(Gun93Registries.guns93.Guns93Mag) then
        return ISInsertMagazine_loadAmmo_Old(self)
    end
    local isLoader = self.magazine:hasTag(Gun93Registries.guns93.SpeedLoader) or self.magazine:hasTag(Gun93Registries.guns93.StripperClip) or self.magazine:hasTag(Gun93Registries.guns93.SpeedStrip)
    if isLoader then
        local ammoNum = self.gun:getMaxAmmo() - self.gun:getCurrentAmmoCount()
        local loaded = 0
        if ammoNum >= self.magazine:getCurrentAmmoCount() then
            if self.magazine:hasTag(Gun93Registries.guns93.SpeedStrip) and self.magazine:getCurrentAmmoCount() > 1 then
                loaded = 2
            else
                loaded = self.magazine:getCurrentAmmoCount()
            end
        elseif not self.magazine:hasTag(Gun93Registries.guns93.SpeedLoader) then
            loaded = ammoNum
        end
        if loaded > 0 then
            self.gun:setCurrentAmmoCount(self.gun:getCurrentAmmoCount() + loaded)
            self.magazine:setCurrentAmmoCount(self.magazine:getCurrentAmmoCount() - loaded)
        end
        DynamicMagClip(self.magazine)
        syncItemFields(self.character, self.magazine)
        syncItemModData(self.character, self.magazine)
        syncHandWeaponFields(self.character, self.gun)
        if self.magazine:hasTag(Gun93Registries.guns93.StripperClip) or self.magazine:hasTag(Gun93Registries.guns93.SpeedStrip) then
            if self.gun:getCurrentAmmoCount() < self.gun:getMaxAmmo() and self.magazine:getCurrentAmmoCount() > 0 then
                Guns93ReloadAnotherStrip(self.character, self.gun, self.magazine)
            end
        end
    else
        self.character:getInventory():Remove(self.magazine)
        self.character:removeFromHands(self.magazine)
        if not self.magazine:hasTag(Gun93Registries.guns93.MoonClip) then
            self.gun:setMagazineType(self.magazine:getFullType())
            self.gun:setMaxAmmo(self.magazine:getMaxAmmo())
            self.gun:setContainsClip(true)
        end
        self.gun:setCurrentAmmoCount(self.magazine:getCurrentAmmoCount())
        if self.magazine:getCategory() == "WeaponPart" then
            self.gun:attachWeaponPart(self.magazine)
        end
        sendRemoveItemFromContainer(self.character:getInventory(), self.magazine)
        syncHandWeaponFields(self.character, self.gun)
    end
    self.character:getInventory():setDirty(true)
	if not isServer() and not isClient() and not self.gun:isRoundChambered() and self.gun:getCurrentAmmoCount() >= self.gun:getAmmoPerShoot() then
		ISTimedActionQueue.addAfter(self, ISRackFirearm:new(self.character, self.gun))
	end
    self.character:clearVariable("isLoading")
end

-- Eject Magazine unload (server for item spawn)
local ISEjectMagazine_start_Old = ISEjectMagazine.start
function ISEjectMagazine:start()
    if not self.gun:hasTag(Gun93Registries.guns93.Guns93) then
        return ISEjectMagazine_start_Old(self)
    end
	if not self.gun:isContainsClip() and not self.gun:getWeaponPart("Moonclip") then
		self:forceStop()
		return
	end
	self:setAnimVariable("WeaponReloadType", tostring(self.gun:getWeaponReloadType()))
	self:setAnimVariable("isUnloading", true)
	self:setOverrideHandModels(self.gun, nil)
	self:setActionAnim(CharacterActionAnims.Reload)
	self.character:reportEvent("EventReloading");
	self:initVars()
end

local ISEjectMagazine_unloadAmmo_Old = ISEjectMagazine.unloadAmmo
function ISEjectMagazine:unloadAmmo()
    if isClient() then return end
    if not self.gun:hasTag(Gun93Registries.guns93.Guns93) then
        return ISEjectMagazine_unloadAmmo_Old(self)
    end
    local newMag = nil
    if Guns93reloadTypeisEnBloc(self.gun) and self.gun:isContainsClip() then
        newMag = instanceItem(self.gun:getMagazineType())
        if self.gun:isRoundChambered() then
            self.gun:setRoundChambered(false)
            self.character:getInventory():AddItem(self.gun:getAmmoType():getItemKey())
        end
        newMag:setCurrentAmmoCount(self.gun:getCurrentAmmoCount())
    elseif self.gun:getWeaponPart("Moonclip") then
        local moonClip = self.gun:getWeaponPart("Moonclip")
        newMag = instanceItem(moonClip:getFullType())
        newMag:setCurrentAmmoCount(self.gun:getCurrentAmmoCount())
        Guns93BrassEject(self.character, self.gun)
        DynamicMagClip(newMag)
        self.gun:detachWeaponPart(moonClip)
    elseif self.gun:isContainsClip() then
        newMag = instanceItem(self.gun:getMagazineType())
        if Guns93reloadTypeisOpenBolt(self.gun) then
            if self.gun:isRoundChambered() then
                self.gun:setRoundChambered(false)
                newMag:setCurrentAmmoCount(self.gun:getCurrentAmmoCount() + 1)
            else
                newMag:setCurrentAmmoCount(self.gun:getCurrentAmmoCount())
            end
        end
        newMag:setCurrentAmmoCount(self.gun:getCurrentAmmoCount())
        DynamicMagClip(newMag)
        local clipPart = self.gun:getWeaponPart("Clip")
        if clipPart then self.gun:detachWeaponPart(clipPart) end
    end
    if newMag then
        self.character:getInventory():AddItem(newMag)
        sendAddItemToContainer(self.character:getInventory(), newMag)
    end
    self.gun:setContainsClip(false)
    self.gun:setCurrentAmmoCount(0)
    syncHandWeaponFields(self.character, self.gun)
    self.character:getInventory():setDirty(true)
end

local ISInsertMagazine_initVars_Old = ISInsertMagazine.initVars
function ISInsertMagazine:initVars()
    if not self.gun:hasTag(Gun93Registries.guns93.Guns93) then
        return ISInsertMagazine_initVars_Old(self)
    end
    ISReloadWeaponAction.setReloadSpeed(self.character, false, self.magazine)
--    if self.magazine:hasTag(Gun93Registries.guns93.StripperClip) and self.gun:haveChamber() then
--        local currentCapacity = self.gun:getCurrentAmmoCount()
--        ISTimedActionQueue.add(ISRackFirearm:new(self.character, self.gun))
--        self.gun:setRoundChambered(false)
--        self.gun:setCurrentAmmoCount(currentCapacity)
--        syncHandWeaponFields(self.character, self.gun)
--    end
end

local ISRackFirearm_rackBullet_Old = ISRackFirearm.rackBullet
function ISRackFirearm:rackBullet()
    if not self.gun:hasTag(Gun93Registries.guns93.Guns93) then
        return ISRackFirearm_rackBullet_Old(self)
    end
    if self.gun:haveChamber() then
        if self.gun:isJammed() and self.gun:checkUnJam(self.character) then
            syncHandWeaponFields(self.character, self.gun)
            return
        end
        if self.gun:isRoundChambered() then
            if not self.gun:isJammed() then
                self:removeBullet()
            elseif self.gun:isSpentRoundChambered() then
                Guns93BrassPicker(self.character, self.gun)
            end
        end
        self.gun:setRoundChambered(false)
        self.gun:setJammed(false)
        if self.gun:getCurrentAmmoCount() >= self.gun:getAmmoPerShoot() then
            self.gun:setRoundChambered(true)
            self.gun:setCurrentAmmoCount(self.gun:getCurrentAmmoCount() - self.gun:getAmmoPerShoot())
            if self.gun:getJamGunChance() > 0 then
                self.gun:checkJam(self.character, true)
            end
        end
    else
        self.gun:setJammed(false)
    end
    syncHandWeaponFields(self.character, self.gun)
end

local ISReloadWeaponAction_setReloadSpeed_old = ISReloadWeaponAction.setReloadSpeed
function ISReloadWeaponAction.setReloadSpeed(character, rack, mag)
    local gun = character:getPrimaryHandItem()
    if gun and not gun:hasTag(Gun93Registries.guns93.Guns93) then
        return ISReloadWeaponAction_setReloadSpeed_old(character, rack)
    end
    local baseReloadSpeed = 0.7
    if rack then
		baseReloadSpeed = baseReloadSpeed + (character:getPerkLevel(Perks.Reloading) * 0.04);
        baseReloadSpeed = baseReloadSpeed - (character:getMoodles():getMoodleLevel(MoodleType.PANIC) * 0.04)
        if gun:hasTag(Gun93Registries.guns93.FastRack) then 
            baseReloadSpeed = baseReloadSpeed * 1.1
        elseif gun:hasTag(Gun93Registries.guns93.SlowRack) then 
            baseReloadSpeed = baseReloadSpeed * 0.9 
        end
        local handguard = gun:getWeaponPart("Handguard")
        if handguard then
            if handguard:hasTag(Gun93Registries.guns93.FastRack) then 
                baseReloadSpeed = baseReloadSpeed * 1.1
            elseif handguard:hasTag(Gun93Registries.guns93.SlowRack) then 
                baseReloadSpeed = baseReloadSpeed * 0.9 
            end
        end
        local chargeHandle = gun:getWeaponPart("ChargeHandle")
        if chargeHandle then
            if chargeHandle:hasTag(Gun93Registries.guns93.FastRack) then 
                baseReloadSpeed = baseReloadSpeed * 1.1
            elseif chargeHandle:hasTag(Gun93Registries.guns93.SlowRack) then 
                baseReloadSpeed = baseReloadSpeed * 0.9 
            end
        end
    else
		baseReloadSpeed = baseReloadSpeed + (character:getPerkLevel(Perks.Reloading) * 0.10);
		baseReloadSpeed = baseReloadSpeed - (character:getMoodles():getMoodleLevel(MoodleType.PANIC) * 0.05);
        local strap = character:getWornItem(ItemBodyLocation.AMMO_STRAP)
        local reloadFast = character:hasEquippedTag(ItemTag.RELOAD_FAST_SHELLS) or character:hasEquippedTag(ItemTag.RELOAD_FAST_BULLETS)
        local shellStrap = strap and strap:getClothingItemName() == "AmmoStrap_Shells"
        local bulletStrap = strap and strap:getClothingItemName() == "AmmoStrap_Bullets"
        local strapFound = false
        if gun then
            local isShell = gun:getAmmoType():getItemKey() == "Base.ShotgunShells" or gun:getAmmoType():getItemKey() == "Base.SlugBullets"
            local isMagazine = gun:getMagazineType() or mag
            if gun:hasTag(Gun93Registries.guns93.FastReload) then 
                baseReloadSpeed = baseReloadSpeed * 1.1
            elseif gun:hasTag(Gun93Registries.guns93.SlowReload) then 
                baseReloadSpeed = baseReloadSpeed * 0.9 
            end
            if isMagazine and (character:hasEquippedTag(ItemTag.RELOAD_FAST_MAGAZINES) or character:hasWornTag(ItemTag.RELOAD_FAST_MAGAZINES)) then
                strapFound = true
            elseif isShell and (character:hasEquippedTag(ItemTag.RELOAD_FAST_SHELLS) or character:hasWornTag(ItemTag.RELOAD_FAST_SHELLS) or shellStrap) then
                strapFound = true
            elseif not isShell and not isMagazine and (character:hasEquippedTag(ItemTag.RELOAD_FAST_BULLETS) or character:hasWornTag(ItemTag.RELOAD_FAST_BULLETS) or bulletStrap) then
                strapFound = true
            end
            if mag then
                if mag:hasTag(Gun93Registries.guns93.SlowMag1) then
                    baseReloadSpeed = baseReloadSpeed * 0.9
                elseif mag:hasTag(Gun93Registries.guns93.SlowMag2) then
                    baseReloadSpeed = baseReloadSpeed * 0.6
                elseif mag:hasTag(Gun93Registries.guns93.SlowMag3) then
                    baseReloadSpeed = baseReloadSpeed * 0.2
                elseif mag:hasTag(Gun93Registries.guns93.FastMag1) then
                    baseReloadSpeed = baseReloadSpeed * 1.1
                elseif mag:hasTag(Gun93Registries.guns93.FastMag2) then
                    baseReloadSpeed = baseReloadSpeed * 1.2
                end
            end
        end
        if strapFound then 
            baseReloadSpeed = baseReloadSpeed * 1.15 
        end
    end
    if character:getVehicle() and character:getVehicle():getDriver() == character then
        baseReloadSpeed = baseReloadSpeed * 0.8
    end
    character:setVariable("ReloadSpeed", baseReloadSpeed)
end

local reloadMagazine = function(playerObj, magazine)
	if not magazine then
		return 0
	end
	local itemKey = magazine:getAmmoType():getItemKey();
	local ammoCount = magazine:getCurrentAmmoCount() + ISInventoryPaneContextMenu.transferBullets(playerObj, itemKey, magazine:getCurrentAmmoCount(), magazine:getMaxAmmo())
	if ammoCount > 0 then
		ISTimedActionQueue.add(ISLoadBulletsInMagazine:new(playerObj, magazine, ammoCount))
	end
	return ammoCount
end

local old_BeginAutomaticReload = ISReloadWeaponAction.BeginAutomaticReload
function ISReloadWeaponAction.BeginAutomaticReload(playerObj, gun)
    if not gun or not gun:hasTag(Gun93Registries.guns93.Guns93) then
        return old_BeginAutomaticReload(playerObj, gun)
    end
    if gun:isContainsClip() or gun:getWeaponPart("Moonclip") then
        ISTimedActionQueue.add(ISEjectMagazine:new(playerObj, gun))
    end
    local bestMag = nil
    local highest = -1
    for i = 1, #MagazineGunSet, 10 do
        if MagazineGunSet[i] == gun:getFullType() then
            for j = 1, 9 do
                local magType = MagazineGunSet[i + j]
                if magType and magType ~= "null" then
                    local items = playerObj:getInventory():getAllTypeRecurse(magType)
                    for k = 0, items:size() - 1 do
                        local mag = items:get(k)
                        if mag and mag:getCurrentAmmoCount() > highest then
                            if not (mag:hasTag(Gun93Registries.guns93.SpeedLoader) and gun:getCurrentAmmoCount() > mag:getCurrentAmmoCount()) then
                                highest = mag:getCurrentAmmoCount()
                                bestMag = mag
                            end
                        end
                    end
                end
            end
        end
    end
    if not bestMag and gun:getMagazineType() then
        local items = playerObj:getInventory():getAllTypeRecurse(gun:getMagazineType())
        for k = 0, items:size() - 1 do
            local mag = items:get(k)
            if mag and mag:getCurrentAmmoCount() > highest then
                if not (mag:hasTag(Gun93Registries.guns93.SpeedLoader) and gun:getCurrentAmmoCount() > mag:getCurrentAmmoCount()) then
                    highest = mag:getCurrentAmmoCount()
                    bestMag = mag
                end
            end
        end
    end
    if bestMag then
        if (bestMag:hasTag(Gun93Registries.guns93.MoonClip) or bestMag:hasTag(Gun93Registries.guns93.SpeedLoader) or bestMag:hasTag(Gun93Registries.guns93.SpeedStrip)) and gun:getSpentRoundCount() > 0 then
            ISTimedActionQueue.add(ISRackFirearm:new(playerObj, gun))
        end
        if bestMag:hasTag(Gun93Registries.guns93.MoonClip) and gun:getCurrentAmmoCount() > 0 then
            ISTimedActionQueue.add(ISUnloadBulletsFromFirearm:new(playerObj, gun))
        end
        if bestMag:getCurrentAmmoCount() > 0 then
            ISInventoryPaneContextMenu.transferIfNeeded(playerObj, bestMag)
            if bestMag:hasTag(Gun93Registries.guns93.StripperClip) then
                ISTimedActionQueue.add(ISUnloadRackFirearm:new(playerObj, gun))
            end
            local readyLoad = true
            if bestMag:hasTag(Gun93Registries.guns93.MoonClip) or bestMag:hasTag(Gun93Registries.guns93.SpeedLoader) then
                if gun:getCurrentAmmoCount() > 0 then
                    readyLoad = false
                end
            end
            if readyLoad then
                ISTimedActionQueue.add(ISInsertMagazine:new(playerObj, gun, bestMag))
            end
            if readyLoad then return end
        end
        if not (bestMag:hasTag(Gun93Registries.guns93.MoonClip) or bestMag:hasTag(Gun93Registries.guns93.SpeedLoader) or bestMag:hasTag(Gun93Registries.guns93.SpeedStrip) or bestMag:hasTag(Gun93Registries.guns93.StripperClip)) then
            local ammoCount = reloadMagazine(playerObj, bestMag)
            if ammoCount > 0 or not gun:isContainsClip() then
                ISInventoryPaneContextMenu.transferIfNeeded(playerObj, bestMag)
                ISTimedActionQueue.add(ISInsertMagazine:new(playerObj, gun, bestMag))
                return
            end
        end
    end
    if not gun:getMagazineType() or (gun:getCurrentAmmoCount() >= gun:getMaxAmmo() and not gun:isJammed()) then
        local ammoCount = ISInventoryPaneContextMenu.transferBullets(playerObj, gun:getAmmoType():getItemKey(), gun:getCurrentAmmoCount(), gun:getMaxAmmo())
        if ammoCount > 0 then
            ISTimedActionQueue.add(ISReloadWeaponAction:new(playerObj, gun))
        end
    end
end

local function SetMagazineStats(player, weapon)
    if not weapon or not weapon:hasTag(Gun93Registries.guns93.Guns93) then return end
    local clip = weapon:getWeaponPart("Clip")
    local magExten1 = weapon:getWeaponPart("BarrelItem")
    local magExten2 = weapon:getWeaponPart("MagExten")
    local hasPart = false
    if clip and (clip:hasTag(Gun93Registries.guns93.Guns93Mag) or clip:hasTag(Gun93Registries.guns93.MagExten)) then
        hasPart = true
    end
    if magExten1 and (magExten1:hasTag(Gun93Registries.guns93.Guns93Mag) or magExten1:hasTag(Gun93Registries.guns93.MagExten)) then
        hasPart = true
    end
    if magExten2 and (magExten2:hasTag(Gun93Registries.guns93.Guns93Mag) or magExten2:hasTag(Gun93Registries.guns93.MagExten)) then
        hasPart = true
    end
    if hasPart then
        if clip and clip:hasTag(Gun93Registries.guns93.Guns93Mag) then
            weapon:setMagazineType(clip:getFullType())
            weapon:setMaxAmmo(clip:getMaxAmmo())
        elseif magExten2 then
            magExten1 = magExten2
        end
        if magExten1 then
            weapon:setMaxAmmo(magExten1:getMaxAmmo())
        end
    end
    if not clip and not magExten1 then
        if weapon:getModData().isBaseAmmo then
            weapon:setMaxAmmo(weapon:getModData().isBaseAmmo)
        end
    end
    player:getInventory():setDirty(true)
end

Events.OnEquipPrimary.Add(SetMagazineStats)
Events.OnGameStart.Add(function()
    local player = getPlayer()
    if player then
        SetMagazineStats(player, player:getPrimaryHandItem())
    end
end)

local function Guns93BullpupStock(weapon, isRemove)
    if isRemove then
        weapon:setWeaponSprite((string.gsub(weapon:getWeaponSprite(), "Bullpup$", "")))
    else
        weapon:setWeaponSprite(weapon:getWeaponSprite() .. "Bullpup")
    end
end

local old_ISRemoveWeaponUpgrade_complete = ISRemoveWeaponUpgrade.complete
function ISRemoveWeaponUpgrade:complete()
    if not self.weapon or not self.weapon:hasTag(Gun93Registries.guns93.Guns93) then
        return old_ISRemoveWeaponUpgrade_complete(self)
    end
    local part = self.weapon:getWeaponPart(self.partType)
    if part:hasTag(Gun93Registries.guns93.BullpupStock) then
        Guns93BullpupStock(self.weapon, true)
    end
    self.weapon:detachWeaponPart(self.character, part)
    if part:hasTag(Gun93Registries.guns93.ToggleItemOff) then
        part = instanceItem(string.gsub(part:getFullType(), "_On$", ""))
        if part:isActivated() then
            part:setActivated(false)
        end
    end
    SetMagazineStats(self.character, self.weapon)
    syncHandWeaponFields(self.character, self.weapon)
    self.character:getInventory():AddItem(part);
    sendAddItemToContainer(self.character:getInventory(), part);
    if self.character:getPrimaryHandItem() == self.weapon then
        self.character:setPrimaryHandItem(nil);
    end
    if self.character:getSecondaryHandItem() == self.weapon then
        self.character:setSecondaryHandItem(nil);
    end
    return true
end

local old_ISUpgradeWeapon_complete = ISUpgradeWeapon.complete
function ISUpgradeWeapon:complete()
    if not self.weapon or not self.weapon:hasTag(Gun93Registries.guns93.Guns93) then
        return old_ISUpgradeWeapon_complete(self)
    end
    if self.part:hasTag(Gun93Registries.guns93.BullpupStock) then
        Guns93BullpupStock(self.weapon, false)
    end
    self.weapon:attachWeaponPart(self.character, self.part)
    SetMagazineStats(self.character, self.weapon)
    syncHandWeaponFields(self.character, self.weapon)
    self.character:getInventory():Remove(self.part);
    sendRemoveItemFromContainer(self.character:getInventory(), self.part);
    if self.character:getPrimaryHandItem() == self.weapon then
        self.character:setPrimaryHandItem(nil);
    end
    if self.character:getSecondaryHandItem() == self.weapon then
        self.character:setSecondaryHandItem(nil);
    end
    return true
end

local function Guns93BreakActionClosed(player, weapon)
    if weapon and tostring(weapon:getWeaponReloadType()) == "doublebarrelshotgun" then
        local sprite = weapon:getWeaponSprite()
        sprite = string.gsub(sprite, "_OPEN", "")
        weapon:setWeaponSprite(sprite)
        player:resetEquippedHandsModels()
    end
end

local function Guns93BreakActionOpen(player, weapon)
    if weapon and tostring(weapon:getWeaponReloadType()) == "doublebarrelshotgun" then
        local sprite = weapon:getWeaponSprite()
        sprite = string.gsub(sprite, "_OPEN", "")
        sprite = sprite .. "_OPEN"
        weapon:setWeaponSprite(sprite)
        player:resetEquippedHandsModels()
    end
end

ISReloadWeaponAction_animEvent_Old = ISReloadWeaponAction.animEvent
function ISReloadWeaponAction.animEvent(self, event, parameter)
    if tostring(event) == "changeWeaponSprite" then
        Guns93BreakActionOpen(self.character, self.gun)
    else
        return ISReloadWeaponAction_animEvent_Old(self, event, parameter)
    end
end

ISReloadWeaponAction_perform_Old = ISReloadWeaponAction.perform
function ISReloadWeaponAction.perform(self)
    if not isServer() then
        Guns93BreakActionClosed(self.character, self.gun)
    end
    return ISReloadWeaponAction_perform_Old(self)
end


ISReloadWeaponAction_complete_Old = ISReloadWeaponAction.complete
function ISReloadWeaponAction.complete(self)
    if isServer() then
        Guns93BreakActionClosed(self.character, self.gun)
    end
    return ISReloadWeaponAction_complete_Old(self)
end

ISReloadWeaponAction_stop_Old = ISReloadWeaponAction.stop
function ISReloadWeaponAction.stop(self)
    Guns93BreakActionClosed(self.character, self.gun)
	return ISReloadWeaponAction_stop_Old(self)
end

ISUnloadBulletsFromFirearm_animEvent_Old = ISUnloadBulletsFromFirearm.animEvent
function ISUnloadBulletsFromFirearm.animEvent(self, event, parameter)
    if tostring(event) == "changeWeaponSprite" then
        Guns93BreakActionOpen(self.character, self.gun)
    else        
        return ISUnloadBulletsFromFirearm_animEvent_Old(self, event, parameter)
    end
end

ISUnloadBulletsFromFirearm_complete_Old = ISUnloadBulletsFromFirearm.complete
function ISUnloadBulletsFromFirearm.complete(self)
    Guns93BreakActionClosed(self.character, self.gun)
	return ISUnloadBulletsFromFirearm_complete_Old(self)
end

ISUnloadBulletsFromFirearm_stop_Old =ISUnloadBulletsFromFirearm.stop
function ISUnloadBulletsFromFirearm.stop(self)
    Guns93BreakActionClosed(self.character, self.gun)
	return ISUnloadBulletsFromFirearm_stop_Old(self)
end

ISRackFirearm_animEvent_Old = ISRackFirearm.animEvent
function ISRackFirearm.animEvent(self, event, parameter)
    if tostring(event) == "changeWeaponSprite" then
        Guns93BreakActionOpen(self.character, self.gun)
    else 
        return ISRackFirearm_animEvent_Old(self, event, parameter)
    end
end

ISRackFirearm_complete_Old = ISRackFirearm.complete
function ISRackFirearm.complete(self)
    Guns93BreakActionClosed(self.character, self.gun)
	return ISRackFirearm_complete_Old(self)
end

ISRackFirearm_stop_Old = ISRackFirearm.stop
function ISUnloadBulletsFromFirearm.stop(self)
    Guns93BreakActionClosed(self.character, self.gun)
	return ISRackFirearm_stop_Old(self)
end

ISUnloadRackFirearm = ISBaseTimedAction:derive("ISUnloadRackFirearm")

function ISUnloadRackFirearm:isValid()
	return true
end

function ISUnloadRackFirearm:start()
	if isClient() then
		self.gun = self.character:getInventory():getItemById(self.gun:getID())
	end
	if not ISReloadWeaponAction.canRack(self.gun) then
		self:forceComplete()
		return
	end
	self:setAnimVariable("WeaponReloadType", tostring(self.gun:getWeaponReloadType()))
	if self.gun:haveChamber() then
		self:setAnimVariable("isRacking", true)
	else
		self:setAnimVariable("isUnloading", true)
	end

	self:setAnimVariable("RackAiming", self.character:isAiming())
	if self.character:isAiming() then
		self.character:setAimingDelay(self.character:getAimingDelay() + self.gun:getAimingTime() * (0.15 - self.character:getPerkLevel(Perks.Reloading)*0.01))
	end
	self:setOverrideHandModels(self.gun, nil)
	self:setActionAnim(CharacterActionAnims.Reload)
	self.character:reportEvent("EventReloading");

	self:ejectSpentRounds()

	self:initVars()
end

function ISUnloadRackFirearm:update()
end

function ISUnloadRackFirearm:rackBullet()
	if self.gun:haveChamber() then
		if self.gun:isJammed() and self.gun:checkUnJam(self.character) then
			syncHandWeaponFields(self.character, self.gun)
			return
		end
		if not self.gun:isJammed() and self.gun:isRoundChambered() then
			self:removeBullet()
		end
		self.gun:setRoundChambered(false)
		self.gun:setJammed(false)
	else
		if not self.gun:isJammed() and self.gun:getCurrentAmmoCount() > 0 then
			self:removeBullet()
			self.gun:setCurrentAmmoCount(self.gun:getCurrentAmmoCount() - self.gun:getAmmoPerShoot())
		end
		self.gun:setJammed(false)
	end
	syncHandWeaponFields(self.character, self.gun)
end

function ISUnloadRackFirearm:removeBullet()
    local itemKey = self.gun:getAmmoType():getItemKey();
	local newBullet = instanceItem(itemKey)
	self.character:getInventory():AddItem(newBullet)
	sendAddItemToContainer(self.character:getInventory(), newBullet)
end

function ISUnloadRackFirearm:ejectSpentRounds()
	if self.gun:getSpentRoundCount() > 0 then
		self.gun:setSpentRoundCount(0)
		syncHandWeaponFields(self.character, self.gun)
	elseif self.gun:isSpentRoundChambered() then
		self.gun:setSpentRoundChambered(false)
		syncHandWeaponFields(self.character, self.gun)
	else
		return
	end
	if self.gun:getShellFallSound() then
		self.character:getEmitter():playSound(self.gun:getShellFallSound())
	end
end

function ISUnloadRackFirearm:initVars()
    ISReloadWeaponAction.setReloadSpeed(self.character, rack, self.magazine)
end

function ISUnloadRackFirearm:stop()
	if self.playedEjectAmmoStartSound and self.gun:getEjectAmmoStopSound() then
		self.character:playSound(self.gun:getEjectAmmoStopSound());
	end
	self.character:clearVariable("isLoading")
	self.character:clearVariable("isRacking")
	self.character:clearVariable("isUnloading")
	self.character:clearVariable("WeaponReloadType")
	self.character:clearVariable("RackAiming")
	ISBaseTimedAction.stop(self)
end

function ISUnloadRackFirearm:perform()
	if self.playedEjectAmmoStartSound and self.gun:getEjectAmmoStopSound() then
		self.character:playSound(self.gun:getEjectAmmoStopSound());
	end
	self.character:clearVariable("isLoading")
	self.character:clearVariable("isRacking")
	self.character:clearVariable("isUnloading")
	self.character:clearVariable("WeaponReloadType")
	self.character:clearVariable("RackAiming")
	-- needed to remove from queue / start next.
	ISBaseTimedAction.perform(self)
end

function ISUnloadRackFirearm:serverStart()
	self:ejectSpentRounds()
	self:initVars()
	emulateAnimEventOnce(self.netAction, ISReloadWeaponAction.getReloadTime(self.character, 100), "rackBullet", nil)
	emulateAnimEventOnce(self.netAction, ISReloadWeaponAction.getReloadTime(self.character, 1200), "rackingFinished", nil)
end

function ISUnloadRackFirearm:getDuration()
	return -1
end

function ISUnloadRackFirearm:complete()
	return true
end

function ISUnloadRackFirearm:animEvent(event, parameter)
	if event == 'unloadFinished' then
		if not isClient() then
			self:rackBullet()
		end
		if isServer() then
			self.netAction:forceComplete()
		else
			self:forceComplete()
		end
	end
	if event == 'rackBullet' then
		if not isClient() then
			self:rackBullet()
		end
	end
	if event == 'rackingFinished' then
		if isServer() then
			self.netAction:forceComplete()
		else
			self:forceComplete()
		end
	end
	if event == 'playReloadSound' then
		if parameter == 'rack' then
			if self.gun:getRackSound() then
				self.character:playSound(self.gun:getRackSound())
			end
		end
		if parameter == 'ejectAmmoStart' then
			if not self.playedEjectAmmoStartSound and self.gun:getEjectAmmoStartSound() then
				self.playedEjectAmmoStartSound = true;
				self.character:playSound(self.gun:getEjectAmmoStartSound());
			end
			return
		end
		if parameter == 'unload' then
			if self.gun:getEjectAmmoSound() then
				self.character:playSound(self.gun:getEjectAmmoSound())
			end
		end
	end
	if event == "changeWeaponSprite" then
		if parameter and parameter ~= '' then
			if parameter ~= 'original' then
				self:setOverrideHandModels(parameter, nil)
			else
				self:setOverrideHandModels(self.gun:getWeaponSprite(), nil)
			end
		end
	end
end

function ISUnloadRackFirearm:new(character, gun)
	local o = ISBaseTimedAction.new(self, character)
	o.stopOnAim = false
	o.stopOnWalk = false
	o.stopOnRun = true
	o.maxTime = o:getDuration()
	o.useProgressBar = false
	o.gun = gun
	return o
end