From a438ec6245935aa1f693d861eeae0fb056a3d705 Mon Sep 17 00:00:00 2001 From: Konstantin Gitschthaler <31093949+MrXonte@users.noreply.github.com> Date: Wed, 29 Jan 2025 10:05:36 +0100 Subject: [PATCH] C4 Rework (#1739) Now scales C4 damage with radius and gives consistent damage through walls regardless of line of sight. Relates to #1729 and #1730 although the calculation for proper Killzone is not yet included, but the safe and killzones are now 100% correct. --------- Co-authored-by: Tim Goll --- CHANGELOG.md | 3 + .../entities/entities/ttt_c4/shared.lua | 83 ++++++------------- lua/ttt2/libraries/game_effects.lua | 70 ++++++++++++++++ 3 files changed, 99 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20265d16ab..1aa621a11c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to TTT2 will be documented here. Inspired by [keep a changel - Added a menu to allow admins to inspect, in detail, how and why roles are distributed as they are (by @nike4613) - Added option to enable team name next to role name on the HUD (by @milkwxter) - Added score event for winning with configurable role parameter (by @MrXonte) +- Added ExplosiveSphereDamage game effect for easy calculation of explosion damage through walls (by @MrXonte) ### Fixed @@ -39,6 +40,7 @@ All notable changes to TTT2 will be documented here. Inspired by [keep a changel - Fixed a few errors in shop error messages (by @Histalek) - Fixed `markerVision`'s registry table being able to contain duplicate obsolete entries, thus fixing potential syncing issues with markers (by @TW1STaL1CKY) - Fixed issue in new Ammo dropping that could cause an error when dropping for modified weapon bases. (by @MrXonte) +- Fixed C4 not showing the correct inflictor when the player is killed (by @TimGoll) ### Changed @@ -60,6 +62,7 @@ All notable changes to TTT2 will be documented here. Inspired by [keep a changel - Moved all role-related admin options into the "Roles" menu (by @nike4613) - Improved description of role layering (by @nike4613) - Improved the role layering menu by showing which role is enabled and which is disabled (by @TimGoll) +- Reworked C4 damage calculation with new gameEffect ExplosiveSphereDamage (by @MrXonte) ## [v0.14.0b](https://github.com/TTT-2/TTT2/tree/v0.14.0b) (2024-09-20) diff --git a/gamemodes/terrortown/entities/entities/ttt_c4/shared.lua b/gamemodes/terrortown/entities/entities/ttt_c4/shared.lua index 0773b41ea2..6996086e1c 100644 --- a/gamemodes/terrortown/entities/entities/ttt_c4/shared.lua +++ b/gamemodes/terrortown/entities/entities/ttt_c4/shared.lua @@ -109,7 +109,7 @@ function ENT:Initialize() end if not self:GetRadius() then - self:SetRadius(1000) + self:SetRadius(1500) end if not self:GetDmg() then @@ -145,55 +145,6 @@ function ENT.SafeWiresForTime(t) end end ---- --- @param Entity dmgowner --- @param Vector center --- @param number radius --- @realm shared -function ENT:SphereDamage(dmgowner, center, radius) - -- It seems intuitive to use FindInSphere here, but that will find all ents - -- in the radius, whereas there exist only ~16 players. Hence it is more - -- efficient to cycle through all those players and do a Lua-side distance - -- check. - - local r = radius * radius -- square so we can compare with dot product directly - - -- pre-declare to avoid realloc - local d = 0.0 - local diff = nil - local dmg = 0 - - local plys = playerGetAll() - for i = 1, #plys do - local ply = plys[i] - if ply:Team() ~= TEAM_TERROR then - continue - end - - -- dot of the difference with itself is distance squared - diff = center - ply:GetPos() - d = diff:Dot(diff) - - if d >= r then - continue - end - - -- deadly up to a certain range, then a quick falloff within 100 units - d = math.max(0, math.sqrt(d) - 490) - dmg = -0.01 * (d * d) + 125 - - local dmginfo = DamageInfo() - dmginfo:SetDamage(dmg) - dmginfo:SetAttacker(dmgowner) - dmginfo:SetInflictor(self) - dmginfo:SetDamageType(DMG_BLAST) - dmginfo:SetDamageForce(center - ply:GetPos()) - dmginfo:SetDamagePosition(ply:GetPos()) - - ply:TakeDamageInfo(dmginfo) - end -end - local c4boom = Sound("c4.explode") --- @@ -244,11 +195,14 @@ function ENT:Explode(tr) r_outer = r_outer / 2.5 end - -- damage through walls - self:SphereDamage(dmgowner, pos, r_inner) - - -- explosion damage - util.BlastDamage(self, dmgowner, pos, r_outer, self:GetDmg()) + gameEffects.ExplosiveSphereDamage( + dmgowner, + ents.Create("weapon_ttt_c4"), + self:GetDmg(), + pos, + r_outer, + r_inner + ) local effect = EffectData() effect:SetStart(pos) @@ -903,9 +857,24 @@ else -- CLIENT local color = COLOR_WHITE - if mvData:GetEntityDistance() > ent:GetRadius() then + --Calculating damage falloff with inverse square method + --100% from 0 to innerRadius + --100% to 0% from innerRadius to outerRadius + --0% from outerRadius to infinity + local dFraction = math.max( + 1.0 + - math.max( + (mvData:GetEntityDistance() - ent:GetRadiusInner()) + / (ent:GetRadius() - ent:GetRadiusInner()), + 0.0 + ), + 0.0 + ) + local dmg = math.Round(ent:GetDmg() * dFraction * dFraction) + + if dmg <= 0 then mvData:AddDescriptionLine(TryT("c4_marker_vision_safe_zone"), COLOR_GREEN) - elseif mvData:GetEntityDistance() > ent:GetRadiusInner() then + elseif dmg < 100 then mvData:AddDescriptionLine(TryT("c4_marker_vision_damage_zone"), COLOR_ORANGE) color = COLOR_ORANGE diff --git a/lua/ttt2/libraries/game_effects.lua b/lua/ttt2/libraries/game_effects.lua index e76d38f886..e49e5144e1 100644 --- a/lua/ttt2/libraries/game_effects.lua +++ b/lua/ttt2/libraries/game_effects.lua @@ -148,6 +148,76 @@ function gameEffects.RadiusDamage(dmginfo, pos, radius, inflictor) end end +-- Creates explosion damage in a sphere through walls. Very useful for making explosives that aren't line of sight based. +-- @param Player attacker The player that causes this explosion. +-- @param Entity inflictor The entity that causes this explosion. +-- @param number damage The maximum damage done by this explosion. +-- @param Vector origin The center of the explosion. +-- @param number outerRadius The outer border for the explosion damage and its falloff. +-- @param number innerRadius The inner border for the explosion damage where falloff starts. +-- @internal +-- @realm server +function gameEffects.ExplosiveSphereDamage( + attacker, + inflictor, + damage, + origin, + outerRadius, + innerRadius +) + -- It seems intuitive to use FindInSphere here, but that will find all ents + -- in the radius, whereas there exist only ~16 players. Hence it is more + -- efficient to cycle through all those players and do a Lua-side distance + -- check. + + if outerRadius < innerRadius then + ErrorNoHalt( + "[Game Effects Explosive Sphere Damage] Outer radius too high! Setting both radi to outer radius." + ) + innerRadius = outerRadius + end + + -- pre-declare to avoid realloc + local d = 0.0 + local dFraction = 0.0 + local diff = nil + local dmg = 0 + local radiDiff = (outerRadius - innerRadius) + local plys = player.GetAll() + for i = 1, #plys do + local ply = plys[i] + + if not IsValid(ply) or not ply:IsTerror() then + continue + end + + diff = origin - (ply:GetPos() + ply:OBBCenter()) + --we are using Length on purpose here. We would need a sqrt somewhere anyway and with this we dont need to square the radi + d = diff:Length() + --we now turn this into a % of damage based of the value of d + --100% from 0 to innerRadius + --100% to 0% from innerRadius to outerRadius + --<0% from outerRadius to infinity + dFraction = 1.0 - math.max((d - innerRadius) / radiDiff, 0.0) + + --Next Iteration if we are outside the radius + if dFraction < 0.0 then + continue + end + + dmg = math.Round(damage * dFraction * dFraction) + + local dmginfo = DamageInfo() + dmginfo:SetDamage(dmg) + dmginfo:SetAttacker(attacker) + dmginfo:SetInflictor(inflictor) + dmginfo:SetDamageType(DMG_BLAST) + dmginfo:SetDamageForce(diff) + dmginfo:SetDamagePosition(ply:GetPos() + ply:OBBCenter()) + ply:TakeDamageInfo(dmginfo) + end +end + -- vFIRE INTEGRATION if SERVER then