From 4100f632a10a9570ff386092546ee42d49a9ef20 Mon Sep 17 00:00:00 2001 From: Ally-for-Hire Date: Sun, 8 Mar 2026 22:51:20 -0500 Subject: [PATCH] Adjust damage protection and ammo cost scaling --- lua/acf/server/sv_acfbase.lua | 83 ++++++++++++-------- lua/acf/shared/sh_ace_functions.lua | 16 ++-- lua/autorun/acf_globals.lua | 116 ++++++++++++++-------------- 3 files changed, 120 insertions(+), 95 deletions(-) diff --git a/lua/acf/server/sv_acfbase.lua b/lua/acf/server/sv_acfbase.lua index 71c85892..d0a8929c 100644 --- a/lua/acf/server/sv_acfbase.lua +++ b/lua/acf/server/sv_acfbase.lua @@ -138,7 +138,7 @@ function ACF_Check( Entity ) return Entity.ACF.Type end -function ACF_Damage( Entity , Energy , FrArea , Angle , Inflictor , Bone, Gun, Type ) +function ACF_Damage( Entity , Energy , FrArea , Angle , Inflictor , Bone, Gun, Type ) local Activated = ACF_Check( Entity ) local CanDo = hook.Run("ACF_BulletDamage", Activated, Entity, Energy, FrArea, Angle, Inflictor, Bone, Gun ) @@ -168,13 +168,23 @@ function ACF_Damage( Entity , Energy , FrArea , Angle , Inflictor , Bone, Gun, T end hook.Run("ACFOnDamage", Entity, Energy, FrArea, Angle, Inflictor, Bone, Gun, Type, hitRes, oldACFTbl) - return hitRes - -end - - - -function ACF_CalcDamage( Entity , Energy , FrArea , Angle , Type) --y=-5/16x + b + return hitRes + +end + +local function ACF_CanDamagePlayer(Target, Attacker) + if not (IsValid(Target) and Target:IsPlayer()) then return true end + if IsValid(Attacker) and Attacker == Target then return true end + if Target:HasGodMode() then return false end + + local canTakeDamage = hook.Run("PlayerShouldTakeDamage", Target, Attacker) + + return canTakeDamage ~= false +end + + + +function ACF_CalcDamage( Entity , Energy , FrArea , Angle , Type) --y=-5/16x + b local HitRes = {} @@ -269,25 +279,26 @@ function ACF_VehicleDamage(Entity, Energy, FrArea, Angle, Inflictor, _, Gun, Typ } } - local HitRes = ACF_CalcDamage( Target , Energy , FrArea , Angle , Type) - local Driver = Entity:GetDriver() - local validd = Driver:IsValid() - - --In case of HitRes becomes NAN. That means theres no damage, so leave it as 0 - if HitRes.Damage ~= HitRes.Damage then HitRes.Damage = 0 end - - if validd then - local dmg = 40 - Driver:TakeDamage( HitRes.Damage * dmg , Inflictor, Gun ) - end - - HitRes.Kill = false - if HitRes.Damage >= Entity.ACF.Health then --Drivers will no longer survive seat destruction - if validd then - Driver:Kill() - end - HitRes.Kill = true - else + local HitRes = ACF_CalcDamage( Target , Energy , FrArea , Angle , Type) + local Driver = Entity:GetDriver() + local validd = Driver:IsValid() + local canDamageDriver = validd and ACF_CanDamagePlayer(Driver, Inflictor) + + --In case of HitRes becomes NAN. That means theres no damage, so leave it as 0 + if HitRes.Damage ~= HitRes.Damage then HitRes.Damage = 0 end + + if canDamageDriver then + local dmg = 40 + Driver:TakeDamage( HitRes.Damage * dmg , Inflictor, Gun ) + end + + HitRes.Kill = false + if HitRes.Damage >= Entity.ACF.Health then --Drivers will no longer survive seat destruction + if canDamageDriver then + Driver:Kill() + end + HitRes.Kill = true + else Entity.ACF.Health = Entity.ACF.Health - HitRes.Damage Entity.ACF.Armour = Entity.ACF.Armour * (0.5 + Entity.ACF.Health / Entity.ACF.MaxHealth / 2) --Simulating the plate weakening after a hit end @@ -309,11 +320,19 @@ function ACF_SquishyDamage(Entity, Energy, FrArea, _, Inflictor, Bone, Gun, Type local BodyArmor = 0 --Thickness of armor to determine if any damage taken. - local IsPly = false - if Entity:IsPlayer() then IsPly = true end - - if IsPly then - BodyArmor = 3 * (1 + Entity:Armor() / 100) --Thickness of armor to determine if any damage taken. Having 200 armor has a 3x body armor mult. + local IsPly = false + if Entity:IsPlayer() then IsPly = true end + if IsPly and not ACF_CanDamagePlayer(Entity, Inflictor) then + return { + Damage = 0, + Overkill = 0, + Loss = 0, + Kill = false + } + end + + if IsPly then + BodyArmor = 3 * (1 + Entity:Armor() / 100) --Thickness of armor to determine if any damage taken. Having 200 armor has a 3x body armor mult. --print("BodyArmorThickness: " .. BodyArmor) end diff --git a/lua/acf/shared/sh_ace_functions.lua b/lua/acf/shared/sh_ace_functions.lua index f514b4e1..28a25c55 100644 --- a/lua/acf/shared/sh_ace_functions.lua +++ b/lua/acf/shared/sh_ace_functions.lua @@ -1961,17 +1961,23 @@ function ACE_GetEntLegacyCost(ent, massOverride) return math.max(rawMm * perMm * hpCostMul, 0) end - -- Missile ammo crates: derive manufacturing cost from missile round points so guidance - -- always affects legacy cost even when ACEPoints isn't initialized yet. + -- Ammo crates: derive manufacturing cost from per-round ammo pricing so + -- manufacturing scales with round performance and the amount stored. if class == "acf_ammo" then local bdata = ent.BulletData - if istable(bdata) and ACE_IsAmmoMissileType(bdata) then - local perRound = ACE_CalcMissileLegacyRoundCost(bdata) + if istable(bdata) then + local perRound + if ACE_IsAmmoMissileType(bdata) then + perRound = ACE_CalcMissileLegacyRoundCost(bdata) + else + perRound = ACE_GetAmmoRoundPoints(bdata) + end + if perRound > 0 then local rounds = tonumber(ent.Capacity) or tonumber(ent.Ammo) or 0 rounds = math.max(rounds, 1) - return perRound * rounds + return perRound * rounds * 10 end end end diff --git a/lua/autorun/acf_globals.lua b/lua/autorun/acf_globals.lua index 8d379a1e..a916e44b 100644 --- a/lua/autorun/acf_globals.lua +++ b/lua/autorun/acf_globals.lua @@ -102,15 +102,15 @@ ACF.BulletIndexLimit = 5000 -- The maximum number of bullets in flight at an ACF.SkyboxGraceZone = 100 -- grace zone for the high angle fire ACF.SkyboxMinCaliber = 5 -ACF.TraceFilter = { -- entities that cause issue with acf and should be not be processed at all - - prop_vehicle_crane = true, - prop_dynamic = true, - npc_strider = true, - sent_prop2mesh = true, - worldspawn = true, --The worldspawn in infinite maps is fake. Since the IsWorld function will not do something to avoid this case, that i will put it here. - -} +ACF.TraceFilter = { -- entities that cause issue with acf and should be not be processed at all + + prop_vehicle_crane = true, + prop_dynamic = true, + npc_strider = true, + sent_prop2mesh = true, + worldspawn = true, --The worldspawn in infinite maps is fake. Since the IsWorld function will not do something to avoid this case, that i will put it here. + +} ACF.DragDiv = 40 -- Drag fudge factor ACF.VelScale = 1 -- Scale factor for the shell velocities in the game world @@ -135,7 +135,7 @@ ACF.MaxWeight = 200000 -- The max weight in kg. ACE.PointCostConfig = ACE.PointCostConfig or { ArmorFrontWeight = 1.0, -- Front armor contribution in armor points. - ArmorSideWeight = 2.2, -- Side armor contribution in armor points. + ArmorSideWeight = 1.8, -- Side armor contribution in armor points. ArmorScale = 4.0, -- Final armor points multiplier. CrewSeatFlat = 250, -- Flat point cost per crew seat entity. MinDetailPoints = 300 -- Minimum points to list an entry in armor tool breakdown. @@ -164,7 +164,7 @@ ACE.AmmoTypeFactors = { AP = 0.5, APHE = 0.6, APDS = 1.0, - APFSDS = 1.2, + APFSDS = 1.05, HVAP = 0.7, HEAT = 0.75, HEATFS = 0.8, @@ -177,9 +177,9 @@ ACE.AmmoTypeFactors = { CAP = 0.6, CHEAT = 0.8, CHE = 0.25, - CHF = 0, - SM = 0, - FLR = 0, + CHF = 1, + SM = 1, + FLR = 1, FL = 0.3, GLATGM = 0.75, ["GLATGM-HE"] = 0.25, @@ -196,11 +196,11 @@ ACE.LegacyMatCostTables = ACE.LegacyMatCostTables or { RHA = 1 } ACE.AmmoCostConfig = { - BaseRoundPts = 340, -- Tuned so 25x APFSDS @ 750mm at 12 RPM is ~3333 pts (120/125mm class). + BaseRoundPts = 340, -- Base per-round scaling before penetration, caliber, ammo type, and RoF threat are applied. RefPen = 700, -- Reference penetration (mm) for pen scaling. RefCaliber = 100, -- Reference caliber (mm) for caliber scaling. - RofKneeRpm = 8, -- RoF knee for saturation: RoF/(RoF+k), using RPM. - MinRofRpm = 6, -- Minimum RPM factored into ROF threat scaling. + RofKneeRpm = 22, -- RoF knee for saturation: RoF/(RoF+k), using RPM. + MinRofRpm = 4, -- Minimum RPM factored into ROF threat scaling. Set to 0 to use actual sustained RPM. RefBlastMass = 6, -- Reference HE filler mass (kg) for blast scaling. BlastExp = 1.1, -- Blast curve exponent. BlastWeight = 0.25, -- Blend weight for blast vs penetration threat. @@ -326,7 +326,7 @@ if SERVER then CreateConVar("sbox_max_acf_ammo", 100) -- ammo limit CreateConVar("sbox_max_acf_misc", 100) -- misc ents limit CreateConVar("sbox_max_acf_rack", 24) -- Racks limit - CreateConVar("sbox_max_ace_crewseat", 100) + CreateConVar("sbox_max_ace_crewseat", 100) CreateConVar("acf_mines_max", 50) -- The mine limit CreateConVar("acf_meshvalue", 1) @@ -436,46 +436,46 @@ if SERVER then cvars.AddChangeCallback("acf_spalling_multipler", ACF_CVarChangeCallback) cvars.AddChangeCallback("acf_gunfire", ACF_CVarChangeCallback) cvars.AddChangeCallback("acf_debris_lifetime", ACF_CVarChangeCallback) -cvars.AddChangeCallback("acf_debris_children", ACF_CVarChangeCallback) -cvars.AddChangeCallback("acf_explosions_scaled_he_max", ACF_CVarChangeCallback) -cvars.AddChangeCallback("acf_explosions_scaled_ents_max", ACF_CVarChangeCallback) -cvars.AddChangeCallback("acf_legacyrecoil", ACF_CVarChangeCallback) -cvars.AddChangeCallback("acf_legality_enginesrequirefuel", ACF_CVarChangeCallback) -cvars.AddChangeCallback("acf_legality_largeenginesneeddriver", ACF_CVarChangeCallback) -cvars.AddChangeCallback("acf_legality_largeenginethreshold", ACF_CVarChangeCallback) -cvars.AddChangeCallback("acf_legality_largegunsneedgunner", ACF_CVarChangeCallback) -cvars.AddChangeCallback("acf_legality_largegunthreshold", ACF_CVarChangeCallback) -cvars.AddChangeCallback("acf_enable_dp", ACF_CVarChangeCallback) - --- Apply archived/server convars at startup so values persist across restarts and reconnects. -local startupSync = { - "acf_healthmod", - "acf_armormod", - "acf_ammomod", - "acf_spalling", - "acf_spalling_multipler", - "acf_gunfire", - "acf_debris_lifetime", - "acf_debris_children", - "acf_explosions_scaled_he_max", - "acf_explosions_scaled_ents_max", - "acf_legacyrecoil", - "acf_legality_enginesrequirefuel", - "acf_legality_largeenginesneeddriver", - "acf_legality_largeenginethreshold", - "acf_legality_largegunsneedgunner", - "acf_legality_largegunthreshold", - "acf_enable_dp" -} - -for _, name in ipairs(startupSync) do - local convar = GetConVar(name) - if convar then - ACF_CVarChangeCallback(name, nil, convar:GetString()) - end -end - -elseif CLIENT then +cvars.AddChangeCallback("acf_debris_children", ACF_CVarChangeCallback) +cvars.AddChangeCallback("acf_explosions_scaled_he_max", ACF_CVarChangeCallback) +cvars.AddChangeCallback("acf_explosions_scaled_ents_max", ACF_CVarChangeCallback) +cvars.AddChangeCallback("acf_legacyrecoil", ACF_CVarChangeCallback) +cvars.AddChangeCallback("acf_legality_enginesrequirefuel", ACF_CVarChangeCallback) +cvars.AddChangeCallback("acf_legality_largeenginesneeddriver", ACF_CVarChangeCallback) +cvars.AddChangeCallback("acf_legality_largeenginethreshold", ACF_CVarChangeCallback) +cvars.AddChangeCallback("acf_legality_largegunsneedgunner", ACF_CVarChangeCallback) +cvars.AddChangeCallback("acf_legality_largegunthreshold", ACF_CVarChangeCallback) +cvars.AddChangeCallback("acf_enable_dp", ACF_CVarChangeCallback) + +-- Apply archived/server convars at startup so values persist across restarts and reconnects. +local startupSync = { + "acf_healthmod", + "acf_armormod", + "acf_ammomod", + "acf_spalling", + "acf_spalling_multipler", + "acf_gunfire", + "acf_debris_lifetime", + "acf_debris_children", + "acf_explosions_scaled_he_max", + "acf_explosions_scaled_ents_max", + "acf_legacyrecoil", + "acf_legality_enginesrequirefuel", + "acf_legality_largeenginesneeddriver", + "acf_legality_largeenginethreshold", + "acf_legality_largegunsneedgunner", + "acf_legality_largegunthreshold", + "acf_enable_dp" +} + +for _, name in ipairs(startupSync) do + local convar = GetConVar(name) + if convar then + ACF_CVarChangeCallback(name, nil, convar:GetString()) + end +end + +elseif CLIENT then ---------------------------------- Clientside Convars ---------------------------------- CreateClientConVar( "acf_enable_lighting", 1, true ) --Should missiles emit light while their motors are burning? Looks nice but hits framerate. Set to 1 to enable, set to 0 to disable, set to another number to set minimum light-size.