Skip to content

Commit

Permalink
C4 Rework (#1739)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
MrXonte and TimGoll authored Jan 29, 2025
1 parent 40b7af8 commit a438ec6
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 57 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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)

Expand Down
83 changes: 26 additions & 57 deletions gamemodes/terrortown/entities/entities/ttt_c4/shared.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")

---
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
70 changes: 70 additions & 0 deletions lua/ttt2/libraries/game_effects.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit a438ec6

Please sign in to comment.