Skip to content

Commit

Permalink
shield rework hotfix (#3710)
Browse files Browse the repository at this point in the history
* fix a nil error with shield_behavior gadget playing scavengers

* fix nil weapondefID causing rare arithmetic error

* fix error with vtol's not having a valid damage type, handle edgecases by defining a 0 fallback value

* make exemptions in the exemptionslist exemptions via customparams

also shufflw around alldefs to handle customparams better

* fix chunk calculation bug, improve performance

the chunks weren't updating correctly which caused infrequent updaates and random performance spike hits

Now it's more consistent and has performance within acceptable range
  • Loading branch information
SethDGamre authored Sep 13, 2024
1 parent 233de7f commit 4d835ae
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 53 deletions.
50 changes: 32 additions & 18 deletions gamedata/alldefs_post.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1454,31 +1454,30 @@ function WeaponDef_Post(name, wDef)
end

if modOptions.shieldsrework == true then
local shieldCollisionExemptions = { --add the name of the weapons (or just the name of the unit followed by _ ) to this table to exempt from shield collision.
'corsilo_', 'armsilo_', 'armthor_empmissile', 'armemp_', 'cortron_', 'corjuno_', 'armjuno_'
}

-- For balance, paralyzers need to do reduced damage to shields, as their raw raw damage is outsized
local paralyzerShieldDamageMultiplier = 0.25

if wDef.shield then
wDef.shield.repulser = false
wDef.shield.exterior = true
end
-- VTOL's may or may not do full damage to shields if not defined in weapondefs
local vtolShieldDamageMultiplier = 0

wDef.interceptedbyshieldtype = 1
local shieldCollisionExemptions = { --add the name of the weapons (or just the name of the unit followed by _ ) to this table to exempt from shield collision.
'corsilo_', 'armsilo_', 'armthor_empmissile', 'armemp_', 'cortron_', 'corjuno_', 'armjuno_'
}

for _, exemption in ipairs(shieldCollisionExemptions) do
if string.find(name, exemption)then
wDef.interceptedbyshieldtype = 2
break
if wDef.damage ~= nil then
-- Due to the engine not handling overkill damage, we have to store the original shield damage values as a customParam for unit_shield_behavior.lua to reference
wDef.customparams = wDef.customparams or {}
if wDef.damage.shields then
wDef.customparams.shield_damage = wDef.damage.shields
elseif wDef.damage.default then
wDef.customparams.shield_damage = wDef.damage.default
elseif wDef.damage.vtol then
wDef.customparams.shield_damage = wDef.damage.vtol * vtolShieldDamageMultiplier
else
wDef.customparams.shield_damage = 0
end
end

if wDef.damage ~= nil and wDef.damage.default ~= nil then
wDef.customparams = wDef.customparams or {}
-- Due to the engone not handling overkill damage, we have to store the original shield damage values as a customParam for unit_shield_behavior.lua to reference
wDef.customparams.shield_damage = wDef.damage.shields or wDef.damage.default


if wDef.paralyzer then
wDef.customparams.shield_damage = wDef.customparams.shield_damage * paralyzerShieldDamageMultiplier
Expand All @@ -1494,6 +1493,21 @@ function WeaponDef_Post(name, wDef)
wDef.customparams.beamtime_damage_reduction_multiplier = 1 / math.floor(wDef.beamtime * Game.gameSpeed)
end
end

if wDef.shield then
wDef.shield.repulser = false
wDef.shield.exterior = true
end

wDef.interceptedbyshieldtype = 1

for _, exemption in ipairs(shieldCollisionExemptions) do
if string.find(name, exemption)then
wDef.interceptedbyshieldtype = 2
wDef.customparams.shield_aoe_penetration = true
break
end
end
end

if modOptions.evocom == true and wDef.weapontype == "DGun" then
Expand Down
70 changes: 35 additions & 35 deletions luarules/gadgets/unit_shield_behaviour.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ if not gadgetHandler:IsSyncedCode() then return end

local defaultDowntime = 1

-- To save on performance, do not perform AoE damage mitigation checks below this threshold, value chosen empirically to negate laser AoE from a Sumo
-- To save on performance, do not perform AoE damage mitigation checks below this threshold, value chosen empirically to negate laser AoE from a Mammoth
local aoeIgnoreThreshold = 11

-- Units half-in/half-out of a shield should not be protected, so need a buffer of non-coverage near the edge, value chosen empirically through testing to avoid having to look up collision volumes
local radiusExclusionBuffer = 10

-- If a unit doesn't have a defined shield damage or default damage, fallbackShieldDamage will be used as a fallback.
local fallbackShieldDamage = 0

local spGetUnitShieldState = Spring.GetUnitShieldState
local spSetUnitShieldState = Spring.SetUnitShieldState
local spGetGameSeconds = Spring.GetGameSeconds
Expand Down Expand Up @@ -52,11 +55,11 @@ for weaponDefID, weaponDef in ipairs(WeaponDefs) do
end

if weaponDef.customParams.beamtime_damage_reduction_multiplier then
local base = weaponDef.customParams.shield_damage
local base = weaponDef.customParams.shield_damage or fallbackShieldDamage
local multiplier = weaponDef.customParams.beamtime_damage_reduction_multiplier
originalShieldDamages[weaponDefID] = math.ceil(base * multiplier)
else
originalShieldDamages[weaponDefID] = weaponDef.customParams.shield_damage
originalShieldDamages[weaponDefID] = weaponDef.customParams.shield_damage or fallbackShieldDamage
end

if weaponDef.type == 'Flame' then
Expand All @@ -80,12 +83,11 @@ end

local function setCoveredUnits(shieldUnitID)
local shieldData = shieldUnitsData[shieldUnitID]

if not shieldData then
removeCoveredUnits(shieldUnitID)
local x, y, z = spGetUnitPosition(shieldUnitID, true)
if not shieldData or not x then
return
else
removeCoveredUnits(shieldUnitID)
local x, y, z = spGetUnitPosition(shieldUnitID, true)
local unitsTable = spGetUnitsInSphere(x, y, z, (shieldData.radius - radiusExclusionBuffer))

for _, unitID in ipairs(unitsTable) do
Expand Down Expand Up @@ -114,9 +116,8 @@ function gadget:UnitFinished(unitID, unitDefID, unitTeam)
shieldCoverageChecked = false, -- Used to prevent expensive unit coverage checks being performed more than once per cycle
radius = shieldUnitDefs[unitDefID].customParams.shield_radius
}
end

setCoveredUnits(unitID)
end

-- Increases performance by reducing global unitDefID lookups
unitDefIDCache[unitID] = unitDefID
Expand Down Expand Up @@ -246,36 +247,35 @@ function gadget:GameFrame(frame)
shieldUnitIndex[shieldUnitsTotalCount] = shieldUnitID
end

shieldCheckChunkSize = math.max(math.ceil(shieldUnitsTotalCount / 10), 1)

if lastShieldCheckedIndex > #shieldUnitIndex then
lastShieldCheckedIndex = 1
end

shieldCheckEndIndex = math.min(lastShieldCheckedIndex + shieldCheckChunkSize - 1, #shieldUnitIndex)
shieldCheckChunkSize = math.max(math.ceil(shieldUnitsTotalCount / 4), 1)
end

for i = lastShieldCheckedIndex, shieldCheckEndIndex do
local shieldUnitID = shieldUnitIndex[i]
local shieldData = shieldUnitsData[shieldUnitID]

if shieldData then
if not shieldData.shieldCoverageChecked then
if shieldData.shieldEnabled then
setCoveredUnits(shieldUnitID)
else
removeCoveredUnits(shieldUnitID)
if frame % 11 == 7 then
for i = lastShieldCheckedIndex, shieldCheckEndIndex do
local shieldUnitID = shieldUnitIndex[i]
local shieldData = shieldUnitsData[shieldUnitID]

if shieldData then
if not shieldData.shieldCoverageChecked then
if shieldData.shieldEnabled then
setCoveredUnits(shieldUnitID)
else
removeCoveredUnits(shieldUnitID)
end
end
end

shieldData.shieldCoverageChecked = false
shieldData.shieldCoverageChecked = false
end
end
end

lastShieldCheckedIndex = shieldCheckEndIndex + 1
lastShieldCheckedIndex = shieldCheckEndIndex + 1

if lastShieldCheckedIndex > #shieldUnitIndex then
lastShieldCheckedIndex = 1
--Spring.Echo(gameSeconds)
end
shieldCheckEndIndex = math.min(lastShieldCheckedIndex + shieldCheckChunkSize - 1, #shieldUnitIndex)
--Spring.Echo("count", shieldUnitsTotalCount, "lastIndex", lastShieldCheckedIndex, "endIndex", shieldCheckEndIndex, "chunk", shieldCheckChunkSize )

if lastShieldCheckedIndex > #shieldUnitIndex then
lastShieldCheckedIndex = 1
end
end

Expand Down Expand Up @@ -304,8 +304,8 @@ function gadget:ShieldPreDamaged(proID, proOwnerID, shieldWeaponNum, shieldUnitI
-- proID isn't nil if hitscan weapons are used, it's actually -1.
if proID > -1 then
weaponDefID = projectileDefIDCache[proID] or spGetProjectileDefID(proID)

shieldData.shieldDamage = shieldData.shieldDamage + originalShieldDamages[weaponDefID]
local newShieldDamage = originalShieldDamages[weaponDefID] or fallbackShieldDamage
shieldData.shieldDamage = shieldData.shieldDamage + newShieldDamage

if flameWeapons[weaponDefID] then
-- Flames aren't destroyed when they hit shields, so need to delete manually
Expand Down

0 comments on commit 4d835ae

Please sign in to comment.