ACF_CustomMod/lua/acf/server/sv_acfballistics.lua

197 lines
7.6 KiB
Lua

ACF.Bullet = {}
ACF.CurBulletIndex = 0
ACF.BulletIndexLimt = 1000 --The maximum number of bullets in flight at any one time
function ACF_CreateBullet( BulletData )
ACF.CurBulletIndex = ACF.CurBulletIndex + 1 --Increment the index
if ACF.CurBulletIndex > ACF.BulletIndexLimt then
ACF.CurBulletIndex = 1
end
local cvarGrav = GetConVar("sv_gravity")
BulletData["Accel"] = Vector(0,0,cvarGrav:GetInt()*-1) --Those are BulletData settings that are global and shouldn't change round to round
BulletData["LastThink"] = SysTime()
BulletData["FlightTime"] = 0
BulletData["TraceBackComp"] = 0
if BulletData["FuseLength"] then
print("Has fuse")
BulletData["InitTime"] = SysTime()
end
if BulletData["Gun"]:IsValid() then --Check the Gun's velocity and add a modifier to the flighttime so the traceback system doesn't hit the originating contraption if it's moving along the shell path
BulletData["TraceBackComp"] = BulletData["Gun"]:GetPhysicsObject():GetVelocity():Dot(BulletData["Flight"]:GetNormalized())
if BulletData["Gun"].sitp_inspace then
BulletData["Accel"] = Vector(0, 0, 0)
BulletData["DragCoef"] = 0
end
--print(BulletData["TraceBackComp"])
end
BulletData["Filter"] = { BulletData["Gun"] }
BulletData["Index"] = ACF.CurBulletIndex
ACF.Bullet[ACF.CurBulletIndex] = table.Copy(BulletData) --Place the bullet at the current index pos
ACF_BulletClient( ACF.CurBulletIndex, ACF.Bullet[ACF.CurBulletIndex], "Init" , 0 )
ACF_CalcBulletFlight( ACF.CurBulletIndex, ACF.Bullet[ACF.CurBulletIndex] )
end
function ACF_ManageBullets()
for Index,Bullet in pairs(ACF.Bullet) do
ACF_CalcBulletFlight( Index, Bullet ) --This is the bullet entry in the table, the Index var omnipresent refers to this
end
end
hook.Add("Think", "ACF_ManageBullets", ACF_ManageBullets)
function ACF_RemoveBullet( Index )
ACF.Bullet[Index] = nil
end
function ACF_CalcBulletFlight( Index, Bullet, BackTraceOverride )
if not Bullet.LastThink then ACF_RemoveBullet( Index ) return end
if BackTraceOverride then Bullet.FlightTime = 0 end
local Time = SysTime()
local DeltaTime = Time - Bullet.LastThink
local Speed = Bullet.Flight:Length()
local Drag = Bullet.Flight:GetNormalized() * (Bullet.DragCoef * Speed^2)/ACF.DragDiv
Bullet.NextPos = Bullet.Pos + (Bullet.Flight * ACF.VelScale * DeltaTime) --Calculates the next shell position
Bullet.Flight = Bullet.Flight + (Bullet.Accel - Drag)*DeltaTime --Calculates the next shell vector
Bullet.StartTrace = Bullet.Pos - Bullet.Flight:GetNormalized()*math.min(ACF.PhysMaxVel*DeltaTime,Bullet.FlightTime*Speed-Bullet.TraceBackComp*DeltaTime)
Bullet.LastThink = Time
Bullet.FlightTime = Bullet.FlightTime + DeltaTime
ACF_DoBulletsFlight( Index, Bullet )
end
function ACF_DoBulletsFlight( Index, Bullet )
local CanDo = hook.Run("ACF_BulletsFlight", Index, Bullet )
if CanDo == false then return end
if Bullet.FuseLength then
local Time = SysTime() - Bullet.IniTime
if Time > Bullet.FuseLength then
print("Explode")
if not util.IsInWorld(Bullet.Pos) then
ACF_RemoveBullet( Index )
else
ACF_BulletClient( Index, Bullet, "Update" , 1 , Bullet.Pos )
ACF_BulletEndFlight = ACF.RoundTypes[Bullet.Type]["endflight"]
ACF_BulletEndFlight( Index, Bullet, Bullet.Pos, Bullet.Flight:GetNormalized() )
end
end
end
if Bullet.SkyLvL then
if (CurTime() - Bullet.LifeTime) > 500 then -- We don't want to calculate bullets that will never come back to map.
ACF_RemoveBullet( Index )
return
end
if Bullet.NextPos.z > Bullet.SkyLvL then
Bullet.Pos = Bullet.NextPos
return
elseif not util.IsInWorld(Bullet.NextPos) then
ACF_RemoveBullet( Index )
return
else
Bullet.SkyLvL = nil
Bullet.LifeTime = nil
Bullet.Pos = Bullet.NextPos
return
end
end
local FlightTr = { }
FlightTr.start = Bullet.StartTrace
FlightTr.endpos = Bullet.NextPos
FlightTr.filter = Bullet.Filter
local FlightRes = util.TraceLine(FlightTr) --Trace to see if it will hit anything
if FlightRes.HitNonWorld then
ACF_BulletPropImpact = ACF.RoundTypes[Bullet.Type]["propimpact"]
local Retry = ACF_BulletPropImpact( Index, Bullet, FlightRes.Entity , FlightRes.HitNormal , FlightRes.HitPos , FlightRes.HitGroup ) --If we hit stuff then send the resolution to the damage function
if Retry == "Penetrated" then --If we should do the same trace again, then do so
ACF_BulletClient( Index, Bullet, "Update" , 2 , FlightRes.HitPos )
ACF_DoBulletsFlight( Index, Bullet )
--Msg("Retrying\n")
elseif Retry == "Ricochet" then
ACF_BulletClient( Index, Bullet, "Update" , 3 , FlightRes.HitPos )
ACF_CalcBulletFlight( Index, Bullet, true )
else --Else end the flight here
ACF_BulletClient( Index, Bullet, "Update" , 1 , FlightRes.HitPos )
ACF_BulletEndFlight = ACF.RoundTypes[Bullet.Type]["endflight"]
ACF_BulletEndFlight( Index, Bullet, FlightRes.HitPos, FlightRes.HitNormal )
end
elseif FlightRes.HitWorld and not FlightRes.HitSky then --If we hit the world then try to see if it's thin enough to penetrate
ACF_BulletWorldImpact = ACF.RoundTypes[Bullet.Type]["worldimpact"]
local Retry = ACF_BulletWorldImpact( Index, Bullet, FlightRes.HitPos, FlightRes.HitNormal )
if Retry == "Penetrated" then --if it is, we soldier on
ACF_BulletClient( Index, Bullet, "Update" , 2 , FlightRes.HitPos )
ACF_CalcBulletFlight( Index, Bullet, true ) --The world ain't going to move, so we say True for the backtrace override
else --If not, end of the line, boyo
ACF_BulletClient( Index, Bullet, "Update" , 1 , FlightRes.HitPos )
ACF_BulletEndFlight = ACF.RoundTypes[Bullet.Type]["endflight"]
ACF_BulletEndFlight( Index, Bullet, FlightRes.HitPos, FlightRes.HitNormal )
end
elseif FlightRes.HitSky then
if FlightRes.HitNormal == Vector(0,0,-1) then
Bullet.SkyLvL = FlightRes.HitPos.z -- Lets save height on which bullet went through skybox. So it will start tracing after falling bellow this level. This will prevent from hitting higher levels of map
Bullet.LifeTime = CurTime()
Bullet.Pos = Bullet.NextPos
else
ACF_RemoveBullet( Index )
end
else --If we didn't hit anything, move the shell and schedule next think
Bullet.Pos = Bullet.NextPos
end
end
function ACF_BulletClient( Index, Bullet, Type, Hit, HitPos )
if Type == "Update" then
local Effect = EffectData()
Effect:SetAttachment( Index ) --Bulet Index
Effect:SetStart( Bullet.Flight/10 ) --Bullet Direction
if Hit > 0 then -- If there is a hit then set the effect pos to the impact pos instead of the retry pos
Effect:SetOrigin( HitPos ) --Bullet Pos
else
Effect:SetOrigin( Bullet.Pos )
end
Effect:SetScale( Hit ) --Hit Type
util.Effect( "ACF_BulletEffect", Effect, true, true )
else
local Effect = EffectData()
local Filler = 0
if Bullet["FillerMass"] then Filler = Bullet["FillerMass"]*15 end
Effect:SetAttachment( Index ) --Bulet Index
Effect:SetStart( Bullet.Flight/10 ) --Bullet Direction
Effect:SetOrigin( Bullet.Pos )
Effect:SetMagnitude( Bullet["Crate"] ) --Encodes the crate the ammo originates from so clientside knows the crate from wich to pull ammo data
Effect:SetScale( 0 )
util.Effect( "ACF_BulletEffect", Effect, true, true )
end
end
function ACF_BulletWorldImpact( Bullet, Index, HitPos, HitNormal )
--You overwrite this with your own function, defined in the ammo definition file
end
function ACF_BulletPropImpact( Bullet, Index, Target, HitNormal, HitPos )
--You overwrite this with your own function, defined in the ammo definition file
end
function ACF_BulletEndFlight( Bullet, Index, HitPos )
--You overwrite this with your own function, defined in the ammo definition file
end