Skip to content

Commit

Permalink
Incendiary: Fixed explosion sometimes missing fire (#1544)
Browse files Browse the repository at this point in the history
Fixes #1427

This pullrequests fixes the issue with incediaries not spawning fire
particles (or spawning them in the next room if there is any) when
exploding close to a wall. The relevant change for this fix is in the
base grenade file:


![image](https://github.com/TTT-2/TTT2/assets/13639408/64c9b037-0143-4c20-9164-ecc40e8fbf98)

This is not a TTT2 issue, it also [exists in vanilla
TTT](https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/terrortown/entities/entities/ttt_basegrenade_proj.lua#L54).
The issue exists because the trace, that should detect the ground, does
not ignore the grenade itself and returns therefore a kind of random
HitNormal. Since the grenade is then moved along the normal prior to
explosion, this often resulted in the grenade being behing a wall and
therefore not spawning any particles.

Adding `self` to the filter would fix this issue as well, but after some
quick thought I decided that `MASK_SOLID_BRUSHONLY` would be a fitting
mask setting here. [[docs]](https://wiki.facepunch.com/gmod/Enums/MASK).

This issue probably existed with ALL GRENADES that inherited from the
weapon base. I wonder why nobody besides me ever reported this. Probably
because you do not notice it during normal chaotic gameplay.

Besides this change I also renamed some variables to be more in-line
with our style, while also moving the fire grenade decal creation to the
server. That way the decal spawns together with the explosion and not a
few moments before. It looks cleaner that way.
  • Loading branch information
TimGoll authored Jun 2, 2024
1 parent 3a70105 commit fd5fe03
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 67 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ All notable changes to TTT2 will be documented here. Inspired by [keep a changel
- Fixed a nil compare error in the DrawHUD function in weapon_tttbasegrenade (by @mexikoedi)
- Fixed players sometimes not receiving their role if they joined late to the game (by @TimGoll)
- Fixed weapon dryfire sound interrupting the weapon's gunshot sound (by @TW1STaL1CKY)
- Fixed incendiaries sometimes exploding without fire (by @TimGoll)
- Fixed scoreboard not showing any body search info on players that changed to forced spec during a round (by @TimGoll)

### Removed
Expand Down
21 changes: 12 additions & 9 deletions gamemodes/terrortown/entities/entities/ttt_basegrenade_proj.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,28 +56,31 @@ end
---
-- @ignore
function ENT:Think()
local etime = self:GetExplodeTime() or 0
if etime ~= 0 and etime < CurTime() then
local timeExplosion = self:GetExplodeTime() or 0

if timeExplosion ~= 0 and timeExplosion < CurTime() then
local thrower = self:GetThrower()

-- if thrower disconnects before grenade explodes, just don't explode
if SERVER and (not IsValid(self:GetThrower())) then
if SERVER and (not IsValid(thrower)) then
self:Remove()
etime = 0

return
end

-- find the ground if it's near and pass it to the explosion
local spos = self:GetPos()
local pos = self:GetPos()
local tr = util.TraceLine({
start = spos,
endpos = spos + Vector(0, 0, -32),
mask = MASK_SHOT_HULL,
filter = self.thrower,
start = pos,
endpos = pos + Vector(0, 0, -32),
mask = MASK_SOLID_BRUSHONLY,
})

local success, err = pcall(self.Explode, self, tr)
if not success then
-- prevent effect spam on Lua error
self:Remove()

ErrorNoHaltWithStack("ERROR CAUGHT: ttt_basegrenade_proj: " .. err .. "\n")
end
end
Expand Down
60 changes: 29 additions & 31 deletions gamemodes/terrortown/entities/entities/ttt_firegrenade_proj.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,46 +32,44 @@ end
---
-- @ignore
function ENT:Explode(tr)
if SERVER then
self:SetNoDraw(true)
self:SetSolid(SOLID_NONE)
self:SetDetonateExact(0)

-- pull out of the surface
if tr.Fraction ~= 1.0 then
self:SetPos(tr.HitPos + tr.HitNormal * 0.6)
end
if CLIENT then
return
end

local pos = self:GetPos()
self:SetNoDraw(true)
self:SetSolid(SOLID_NONE)

if util.PointContents(pos) == CONTENTS_WATER then
self:Remove()
return
end
-- pull out of the surface
if tr.Fraction ~= 1.0 then
self:SetPos(tr.HitPos + tr.HitNormal * 0.6)
end

local effect = EffectData()
effect:SetStart(pos)
effect:SetOrigin(pos)
effect:SetScale(self:GetRadius() * 0.3)
effect:SetRadius(self:GetRadius())
effect:SetMagnitude(self.dmg)
local pos = self:GetPos()

if tr.Fraction ~= 1.0 then
effect:SetNormal(tr.HitNormal)
end
if util.PointContents(pos) == CONTENTS_WATER then
self:Remove()

util.Effect("Explosion", effect, true, true)
return
end

util.BlastDamage(self, self:GetThrower(), pos, self:GetRadius(), self:GetDmg())
local effect = EffectData()
effect:SetStart(pos)
effect:SetOrigin(pos)
effect:SetScale(self:GetRadius() * 0.3)
effect:SetRadius(self:GetRadius())
effect:SetMagnitude(self.dmg)

gameEffects.StartFires(pos, tr, 10, 20, false, self:GetThrower(), 500, false, 128, 2)
if tr.Fraction ~= 1.0 then
effect:SetNormal(tr.HitNormal)
end

self:SetDetonateExact(0)
util.PaintDown(pos, "Scorch", self)
util.Effect("Explosion", effect, true, true)
util.BlastDamage(self, self:GetThrower(), pos, self:GetRadius(), self:GetDmg())

self:Remove()
else
local spos = self:GetPos()
util.PaintDown(spos, "Scorch", self)
gameEffects.StartFires(pos, tr, 10, 20, false, self:GetThrower(), 500, false, 128, 2)

self:SetDetonateExact(0)
end
self:Remove()
end
59 changes: 32 additions & 27 deletions lua/ttt2/libraries/game_effects.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
-- A library to consolidate some common effects code.
-- @author EntranceJew
-- @module gameEffects

if SERVER then
AddCSLuaFile()
end
Expand All @@ -11,35 +12,36 @@ gameEffects = {}
---
-- Create a bundle of fires all from a central location.
-- This is used for incendiary grenades or C4 detonation.
-- @param Vector pos The position the fires should originate from.
-- @param TraceResult tr A trace to orient the creation of the fires around.
-- @param number num The number of individual balls of fire that should be created.
-- @param number lifetime The base lifetime of all fires in the bundle.
-- @param boolean explode Should the fires explode when they reach the end of their lives?
-- @param nil|Player dmgowner The player to attribute the fire damage to.
-- @param number spread_force The force that each fire will be flung with.
-- @param boolean immobile If true, fires will become stationary once they begin burning.
-- @param number size The physical scale of the fires.
-- @param number lifetime_variance The amount each lifetime for each fire can vary.
-- @return table A table full of the fire entities.
-- @param Vector pos The position the fires should originate from
-- @param TraceResult tr A trace to orient the creation of the fires around
-- @param number amount The number of individual balls of fire that should be created
-- @param number lifetime The base lifetime of all fires in the bundle
-- @param boolean explode Should the fires explode when they reach the end of their lives
-- @param nil|Player dmgowner The player to attribute the fire damage to
-- @param number forceSpread The force that each fire will be flung with
-- @param boolean immobile If true, fires will become stationary once they begin burning
-- @param number size The physical scale of the fires
-- @param number lifetimeVariance The amount each lifetime for each fire can vary
-- @return table A table full of the fire entities
-- @realm shared
function gameEffects.StartFires(
pos,
tr,
num,
amount,
lifetime,
explode,
dmgowner,
spread_force,
forceSpread,
immobile,
size,
lifetime_variance
lifetimeVariance
)
local flames = {}
for i = 1, num do

for i = 1, amount do
local ang = Angle(-math.Rand(0, 180), math.Rand(0, 360), math.Rand(0, 360))
local vstart = pos + tr.HitNormal * 64
local ttl = lifetime + math.Rand(-lifetime_variance, lifetime_variance)
local ttl = lifetime + math.Rand(-lifetimeVariance, lifetimeVariance)

local flame = ents.Create("ttt_flame")
flame:SetPos(vstart)
Expand All @@ -62,7 +64,7 @@ function gameEffects.StartFires(
if IsValid(phys) then
-- the balance between mass and force is subtle, be careful adjusting
phys:SetMass(2)
phys:ApplyForceCenter(ang:Forward() * spread_force)
phys:ApplyForceCenter(ang:Forward() * forceSpread)
phys:AddAngleVelocity(Vector(ang.p, ang.r, ang.y))
end

Expand All @@ -74,14 +76,14 @@ end

---
-- Creates a single point of fire.
-- @param Vector pos The position to create the fire at.
-- @param number scale Controls the height of the flame more than its radius. Informs the size.
-- @param number life_span How long a fire will burn for.
-- @param nil|Entity owner The creator of the fire.
-- @param nil|Entity parent The thing to attach the fire to.
-- @return nil|Entity The fire it created, or nil if it was merged / couldn't be created.
-- @param Vector pos The position to create the fire at
-- @param number scale Controls the height of the flame more than its radius. Informs the size
-- @param number lifetime How long a fire will burn for
-- @param nil|Entity owner The creator of the fire
-- @param nil|Entity parent The thing to attach the fire to
-- @return nil|Entity The fire it created, or nil if it was merged / couldn't be created
-- @realm server
function gameEffects.SpawnFire(pos, scale, life_span, owner, parent)
function gameEffects.SpawnFire(pos, scale, lifetime, owner, parent)
local fire = ents.Create("env_fire")

if not IsValid(fire) then
Expand All @@ -91,12 +93,15 @@ function gameEffects.SpawnFire(pos, scale, life_span, owner, parent)
fire:SetParent(parent)
fire:SetOwner(owner)
fire:SetPos(pos)

--no glow + delete when out + start on + last forever
fire:SetKeyValue("spawnflags", tostring(128 + 32 + 4 + 2 + 1))

-- hardly controls size, hitbox is goofy, impossible to work with
fire:SetKeyValue("firesize", tostring(scale))
fire:SetKeyValue("health", tostring(life_span))
fire:SetKeyValue("health", tostring(lifetime))
fire:SetKeyValue("ignitionpoint", "64")

-- don't hurt the player because we're managing the hurtbox ourselves
fire:SetKeyValue("damagescale", "0")
fire:Spawn()
Expand All @@ -106,8 +111,8 @@ function gameEffects.SpawnFire(pos, scale, life_span, owner, parent)
end

---
-- greatly simplified version of SDK's game_shard/gamerules.cpp:RadiusDamage
-- does no block checking, radius should be very small
-- Greatly simplified version of SDK's game_shard/gamerules.cpp:RadiusDamage
-- does no block checking, radius should be very small.
-- @note only hits players!
-- @param DamageInfo dmginfo
-- @param Vector pos
Expand Down

0 comments on commit fd5fe03

Please sign in to comment.