local OFHotBrassPatch = { patched = false, } local AMMO_TYPE_ALIAS_BY_ITEM = { ["Base.9x39Bullets"] = "9x39", ["Base.Bullets22LR"] = "22LR", ["Base.Bullets50Magnum"] = "50AE", ["Base.762x54rBullets"] = "762x54R", ["Base.792x57Bullets"] = "792x57Maus", -- Common spelling variants seen in third-party weapon scripts. ["Base.308Bulets"] = "308Win", ["Base.762x54rBulets"] = "762x54R", } local function isPatchToggleEnabled() local vars = SandboxVars and SandboxVars.OpinionatedFirearms if vars and vars.HandleHotBrassCasingSpawnUseAmmoMaker ~= nil then return vars.HandleHotBrassCasingSpawnUseAmmoMaker == true end -- Backward compatibility for existing worlds. if vars and vars.HBVCEFAmmoMakerPatch ~= nil then return vars.HBVCEFAmmoMakerPatch == true end return true end local function isSessionEligible() return isPatchToggleEnabled() end local function getAmmoMakerFiredFromAmmoDataKey(ammoDataKey) if type(ammoDataKey) ~= "string" or ammoDataKey == "" then return nil end if type(ammoMakerAmmoData) ~= "table" or type(ammoMakerAmmoParts) ~= "table" then return nil end local ammoData = ammoMakerAmmoData[ammoDataKey] if type(ammoData) ~= "table" then return nil end local casingType = ammoData.casingType if type(casingType) ~= "string" or casingType == "" then return nil end local partData = ammoMakerAmmoParts[casingType] if type(partData) ~= "table" then return nil end local firedType = partData.partFired or partData.partOld if type(firedType) ~= "string" or firedType == "" then return nil end return firedType end local function getAmmoMakerFiredFromItemKey(ammoType) if type(ammoType) ~= "string" or ammoType == "" then return nil end if type(ammoMakerAmmoTypes) ~= "table" then return nil end local typeData = ammoMakerAmmoTypes[ammoType] if type(typeData) ~= "table" or type(typeData.ammoTypes) ~= "table" or #typeData.ammoTypes == 0 then return nil end local activeIndex = 1 if type(typeData.modIds) == "table" and type(ammoMakerCompatibleMods) == "table" then for i = 1, #typeData.modIds do local modId = typeData.modIds[i] if ammoMakerCompatibleMods[modId] == true then activeIndex = i end end end if activeIndex < 1 or activeIndex > #typeData.ammoTypes then activeIndex = 1 end local activeAmmoKey = typeData.ammoTypes[activeIndex] local activeFiredType = getAmmoMakerFiredFromAmmoDataKey(activeAmmoKey) if activeFiredType then return activeFiredType end for i = 1, #typeData.ammoTypes do local fallbackFiredType = getAmmoMakerFiredFromAmmoDataKey(typeData.ammoTypes[i]) if fallbackFiredType then return fallbackFiredType end end return nil end local function getAmmoMakerFiredCasing(ammoType) if type(ammoType) ~= "string" or ammoType == "" then return nil end local directFiredType = getAmmoMakerFiredFromItemKey(ammoType) if directFiredType then return directFiredType end local aliasAmmoDataKey = AMMO_TYPE_ALIAS_BY_ITEM[ammoType] if aliasAmmoDataKey then local aliasFiredType = getAmmoMakerFiredFromAmmoDataKey(aliasAmmoDataKey) if aliasFiredType then return aliasFiredType end end local lowerAmmoType = string.lower(ammoType) for aliasItemType, ammoDataKey in pairs(AMMO_TYPE_ALIAS_BY_ITEM) do if string.lower(aliasItemType) == lowerAmmoType then local aliasFiredType = getAmmoMakerFiredFromAmmoDataKey(ammoDataKey) if aliasFiredType then return aliasFiredType end end end if type(ammoMakerAmmoTypes) == "table" then for itemType, typeData in pairs(ammoMakerAmmoTypes) do if type(itemType) == "string" and string.lower(itemType) == lowerAmmoType then if type(typeData) == "table" and type(typeData.ammoTypes) == "table" then for i = 1, #typeData.ammoTypes do local fallbackFiredType = getAmmoMakerFiredFromAmmoDataKey(typeData.ammoTypes[i]) if fallbackFiredType then return fallbackFiredType end end end end end end return nil end local function applyPatch() if OFHotBrassPatch.patched then return true end if not isSessionEligible() then return false end if type(SpentCasingPhysics) ~= "table" then return false end if type(SpentCasingPhysics.getItemToEject) ~= "function" then return false end if type(SpentCasingPhysics.doSpawnCasing) ~= "function" then return false end local originalGetItemToEject = SpentCasingPhysics.getItemToEject local originalDoSpawnCasing = SpentCasingPhysics.doSpawnCasing SpentCasingPhysics.getItemToEject = function(ammoType) local mappedType = getAmmoMakerFiredCasing(ammoType) if mappedType then return mappedType end return originalGetItemToEject(ammoType) end SpentCasingPhysics.doSpawnCasing = function(player, weapon, params, racking, optionalItem) if not optionalItem and weapon and weapon.getAmmoType then local ammoTypeObj = weapon:getAmmoType() local ammoType = ammoTypeObj and ammoTypeObj.getItemKey and ammoTypeObj:getItemKey() or ammoTypeObj if ammoType then local mappedType = getAmmoMakerFiredCasing(tostring(ammoType)) if mappedType then optionalItem = mappedType if racking then -- Force rack ejects to stay as empties for this patch. racking = false end end end end return originalDoSpawnCasing(player, weapon, params, racking, optionalItem) end OFHotBrassPatch.patched = true return true end local function tryPatchOnTick() if OFHotBrassPatch.patched then Events.OnTick.Remove(tryPatchOnTick) return end applyPatch() end Events.OnTick.Add(tryPatchOnTick)