Skip to content

Commit

Permalink
C4 Rework (TTT-2#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 TTT-2#1729 and TTT-2#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
2 people authored and Histalek committed Jan 31, 2025
1 parent e7e146c commit 549fd95
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 549fd95

Please sign in to comment.