Files
OpinionatedFirearms/common/media/lua/server/OpinionatedFirearms_HotBrassAmmoMakerPatch.lua
2026-02-16 00:33:14 -05:00

222 lines
6.3 KiB
Lua

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)