add GGS Support

This commit is contained in:
2026-02-16 00:33:14 -05:00
parent 53b0b4317d
commit 06593805a7
10 changed files with 686 additions and 87 deletions

View File

@@ -0,0 +1,97 @@
local OFGGSCasingDisablePatch = {
patched = false,
hasDebugStackInfo = type(debug) == "table" and type(debug.getinfo) == "function",
}
local BLOCKED_GGS_CASING_TYPES = {
["Base.pistol_casing"] = true,
["Base.revolver_casing"] = true,
["Base.rifle_casing"] = true,
["Base.shells_casing"] = true,
}
local function isGgsShellEmitterCall()
if not OFGGSCasingDisablePatch.hasDebugStackInfo then
return false
end
for stackLevel = 3, 10 do
local info = debug.getinfo(stackLevel, "S")
if not info then
break
end
local source = info.source
if type(source) == "string" and string.find(source, "GGS_ShellCasingEmitter.lua", 1, true) then
return true
end
end
return false
end
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 applyPatch()
if OFGGSCasingDisablePatch.patched then
return true
end
if not isPatchToggleEnabled() then
OFGGSCasingDisablePatch.patched = true
return true
end
if type(__classmetatables) ~= "table" then
return false
end
if not zombie or not zombie.iso or not zombie.iso.IsoGridSquare or not zombie.iso.IsoGridSquare.class then
return false
end
local squareMetatable = __classmetatables[zombie.iso.IsoGridSquare.class]
if not squareMetatable or type(squareMetatable.__index) ~= "table" then
return false
end
local originalAddWorldInventoryItem = squareMetatable.__index.AddWorldInventoryItem
if type(originalAddWorldInventoryItem) ~= "function" then
return false
end
squareMetatable.__index.AddWorldInventoryItem = function(square, itemType, ...)
if BLOCKED_GGS_CASING_TYPES[itemType] and
(not OFGGSCasingDisablePatch.hasDebugStackInfo or isGgsShellEmitterCall())
then
return nil
end
return originalAddWorldInventoryItem(square, itemType, ...)
end
OFGGSCasingDisablePatch.patched = true
return true
end
local function tryPatchOnTick()
if applyPatch() then
Events.OnTick.Remove(tryPatchOnTick)
end
end
if not applyPatch() then
Events.OnTick.Add(tryPatchOnTick)
end

View File

@@ -2,8 +2,24 @@ 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
@@ -15,92 +31,123 @@ 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 function getAmmoTypeData()
if type(ammoMakerAmmoTypes) ~= "table" then
return nil
end
local data = ammoMakerAmmoTypes[ammoType]
if type(data) ~= "table" or type(data.ammoTypes) ~= "table" or #data.ammoTypes == 0 then
return nil
end
return data
local directFiredType = getAmmoMakerFiredFromItemKey(ammoType)
if directFiredType then
return directFiredType
end
local function getActiveAmmoData(typeData)
if type(typeData) ~= "table" or type(typeData.ammoTypes) ~= "table" or type(ammoMakerAmmoData) ~= "table" then
return nil
local aliasAmmoDataKey = AMMO_TYPE_ALIAS_BY_ITEM[ammoType]
if aliasAmmoDataKey then
local aliasFiredType = getAmmoMakerFiredFromAmmoDataKey(aliasAmmoDataKey)
if aliasFiredType then
return aliasFiredType
end
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
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
if activeIndex < 1 or activeIndex > #typeData.ammoTypes then
activeIndex = 1
end
local activeAmmoKey = typeData.ammoTypes[activeIndex]
if type(activeAmmoKey) == "string" then
local activeAmmoData = ammoMakerAmmoData[activeAmmoKey]
if type(activeAmmoData) == "table" then
return activeAmmoData
end
end
for i = 1, #typeData.ammoTypes do
local ammoKey = typeData.ammoTypes[i]
if type(ammoKey) == "string" then
local ammoData = ammoMakerAmmoData[ammoKey]
if type(ammoData) == "table" then
return ammoData
end
end
end
return nil
end
local function partDataToFiredType(partData)
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
if type(ammoMakerAmmoParts) ~= "table" then
return nil
end
local ammoTypeData = getAmmoTypeData()
if not ammoTypeData then
return nil
end
local ammoData = getActiveAmmoData(ammoTypeData)
if type(ammoData) == "table" and type(ammoData.casingType) == "string" then
local firedType = partDataToFiredType(ammoMakerAmmoParts[ammoData.casingType])
if firedType then
return firedType
end
end
return nil
@@ -141,7 +188,8 @@ local function applyPatch()
SpentCasingPhysics.doSpawnCasing = function(player, weapon, params, racking, optionalItem)
if not optionalItem and weapon and weapon.getAmmoType then
local ammoType = weapon:getAmmoType()
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