mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
Upload
This commit is contained in:
403
addons/tfa-base/lua/tfa/ballistics/bullet.lua
Normal file
403
addons/tfa-base/lua/tfa/ballistics/bullet.lua
Normal file
@@ -0,0 +1,403 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
-- Copyright (c) 2018-2020 TFA Base Devs
|
||||
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
-- of this software and associated documentation files (the "Software"), to deal
|
||||
-- in the Software without restriction, including without limitation the rights
|
||||
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
-- copies of the Software, and to permit persons to whom the Software is
|
||||
-- furnished to do so, subject to the following conditions:
|
||||
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
|
||||
local vector_origin = Vector()
|
||||
|
||||
--[[Bullet Struct:
|
||||
[BULLET_ID] = {
|
||||
["owner"] = Entity, --used for dmginfo SetAttacker
|
||||
["inflictor"] = Entity, --used for dmginfo SetInflictor
|
||||
["damage"] = Double, --floating point number representing inflicted damage
|
||||
["force"] = Double,
|
||||
["pos"] = Vector, --vector representing current position
|
||||
["velocity"] = Vector, --vector representing movement velocity
|
||||
["model"] = String --optional variable representing the given model,
|
||||
["bul"] = {} --optional table containing bullet data,
|
||||
["smokeparticle"] = String, --smoke particle name from within pcf
|
||||
["bulletOverride"] = Bool --disable coming out of gun barrel on clientside
|
||||
}
|
||||
]]
|
||||
local BallisticBullet = {
|
||||
["owner"] = NULL,
|
||||
["inflictor"] = NULL,
|
||||
["damage"] = 0,
|
||||
["force"] = 0,
|
||||
["pos"] = vector_origin,
|
||||
["velocity"] = vector_origin,
|
||||
["model"] = "models/bullets/w_pbullet1.mdl",
|
||||
["bul"] = {},
|
||||
["delete"] = false,
|
||||
["smokeparticle"] = "tfa_bullet_smoke_tracer"
|
||||
}
|
||||
|
||||
local traceRes = {}
|
||||
|
||||
local traceData = {
|
||||
mask = MASK_SHOT,
|
||||
collisiongroup = COLLISION_GROUP_NONE,
|
||||
ignoreworld = false,
|
||||
output = traceRes
|
||||
}
|
||||
|
||||
local MASK_SHOT_NOWATER = MASK_SHOT
|
||||
|
||||
--main update block
|
||||
function BallisticBullet:Update(delta)
|
||||
if self.delete then return end
|
||||
self:_setup()
|
||||
if self.delete then return end
|
||||
|
||||
local realdelta = (delta - self.last_update) / TFA.Ballistics.SubSteps
|
||||
self.last_update = delta
|
||||
|
||||
local newPos = self:_getnewPosition(realdelta)
|
||||
newPos = self:_checkWater(realdelta, newPos)
|
||||
self:_accelerate(realdelta)
|
||||
self:_moveSafe(newPos)
|
||||
end
|
||||
|
||||
--internal function for sanity checks, etc.
|
||||
function BallisticBullet:_setup()
|
||||
self.creationTime = CurTime()
|
||||
|
||||
if (not IsValid(self.owner)) or (not IsValid(self.inflictor)) then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
if CurTime() > self.creationTime + TFA.Ballistics.BulletLife then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
self.playerOwned = self.owner.IsPlayer and self.owner:IsPlayer()
|
||||
self.startVelocity = self.velocity:Length()
|
||||
self.startDamage = self.damage
|
||||
end
|
||||
|
||||
function BallisticBullet:_think()
|
||||
if (not IsValid(self.owner)) or (not IsValid(self.inflictor)) then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
if CurTime() > self.creationTime + TFA.Ballistics.BulletLife then
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
--internal function for calculating position change
|
||||
function BallisticBullet:_getnewPosition(delta)
|
||||
--verlet
|
||||
return self.pos + (self.velocity + TFA.Ballistics.Gravity * delta * 0.5) * delta
|
||||
end
|
||||
|
||||
--internal function for handling water
|
||||
function BallisticBullet:_checkWater(delta, target)
|
||||
local newPos = target
|
||||
traceData.start = self.pos
|
||||
traceData.endpos = newPos
|
||||
traceData.filter = {self.owner, self.inflictor}
|
||||
traceData.mask = MASK_WATER
|
||||
util.TraceLine(traceData)
|
||||
|
||||
if traceRes.Hit and traceRes.Fraction < 1 and traceRes.Fraction > 0 and not self.Underwater then
|
||||
self.Underwater = true
|
||||
newPos = traceRes.HitPos + traceRes.Normal
|
||||
self.velocity = self.velocity / TFA.Ballistics.WaterEntranceResistance
|
||||
local fx = EffectData()
|
||||
fx:SetOrigin(newPos)
|
||||
local sc = math.sqrt(self.damage / 28) * 6
|
||||
fx:SetScale(sc)
|
||||
util.Effect("gunshotsplash", fx)
|
||||
end
|
||||
|
||||
return newPos
|
||||
end
|
||||
|
||||
--internal function for handling acceleration
|
||||
local function GetWind()
|
||||
return vector_origin
|
||||
end
|
||||
|
||||
if StormFox and StormFox.Version then
|
||||
if StormFox.Version < 2 then -- SF1
|
||||
local SF_GetNetworkData = StormFox.GetNetworkData
|
||||
|
||||
function GetWind()
|
||||
local windSpeed = SF_GetNetworkData("Wind") * TFA.Ballistics.UnitScale
|
||||
local windAng = Angle(0, SF_GetNetworkData("WindAngle"), 0)
|
||||
|
||||
return windSpeed * windAng:Forward():GetNormalized()
|
||||
end
|
||||
elseif StormFox.Wind then -- SF2
|
||||
local SFW_GetForce = StormFox.Wind.GetForce
|
||||
local SFW_GetYaw = StormFox.Wind.GetYaw
|
||||
|
||||
function GetWind()
|
||||
local windSpeed = SFW_GetForce() * TFA.Ballistics.UnitScale
|
||||
local windAng = Angle(0, SFW_GetYaw(), 0)
|
||||
|
||||
return windSpeed * windAng:Forward():GetNormalized()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BallisticBullet:_accelerate(delta)
|
||||
local dragDensity = self.Underwater and TFA.Ballistics.WaterResistance or TFA.Ballistics.AirResistance
|
||||
local drag = -self.velocity:GetNormalized() * self.velocity:Length() * self.velocity:Length() * 0.00006 * dragDensity
|
||||
local wind = GetWind()
|
||||
|
||||
if self.Underwater then
|
||||
self.velocity = self.velocity / (1 + TFA.Ballistics.WaterResistance * delta)
|
||||
end
|
||||
|
||||
self.velocity = self.velocity + (TFA.Ballistics.Gravity + drag + wind) * delta
|
||||
self.damage = self.startDamage * math.sqrt(self.velocity:Length() / self.startVelocity)
|
||||
end
|
||||
|
||||
local IsInWorld, IsInWorld2
|
||||
|
||||
do
|
||||
local tr = {collisiongroup = COLLISION_GROUP_WORLD}
|
||||
|
||||
function IsInWorld2(pos)
|
||||
tr.start = pos
|
||||
tr.endpos = pos
|
||||
return not util.TraceLine(tr).AllSolid
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
IsInWorld = IsInWorld2
|
||||
else
|
||||
IsInWorld = util.IsInWorld
|
||||
end
|
||||
|
||||
--internal function for moving with collision test
|
||||
function BallisticBullet:_moveSafe(newPos)
|
||||
if not self.tr_filter then
|
||||
if IsValid(self.IgnoreEntity) then
|
||||
self.tr_filter = {self.owner, self.inflictor, self.IgnoreEntity}
|
||||
else
|
||||
self.tr_filter = {self.owner, self.inflictor}
|
||||
end
|
||||
end
|
||||
|
||||
traceData.start = self.pos
|
||||
traceData.endpos = newPos + (newPos - self.pos):GetNormalized()
|
||||
traceData.filter = self.tr_filter
|
||||
traceData.mask = MASK_SHOT_NOWATER
|
||||
|
||||
--collision trace
|
||||
if self.playerOwned then
|
||||
self.owner:LagCompensation(true)
|
||||
end
|
||||
|
||||
util.TraceLine(traceData)
|
||||
|
||||
if self.playerOwned then
|
||||
self.owner:LagCompensation(false)
|
||||
end
|
||||
|
||||
--collision check
|
||||
if traceRes.Hit and traceRes.Fraction < 1 and traceRes.Fraction > 0 then
|
||||
self:Impact(traceRes)
|
||||
elseif IsInWorld(newPos) then
|
||||
self.pos = newPos
|
||||
else
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
--called when hitting something, or manually if necessary
|
||||
function BallisticBullet:Impact(tr)
|
||||
self.pos = tr.HitPos
|
||||
self:Remove()
|
||||
|
||||
if CLIENT and (game.SinglePlayer() or self.owner ~= LocalPlayer()) then return end
|
||||
|
||||
if tr.HitSky then return end
|
||||
local vn = self.velocity:GetNormalized()
|
||||
|
||||
local bul = {
|
||||
["Damage"] = self.damage,
|
||||
["Force"] = self.force,
|
||||
["Num"] = 1,
|
||||
["Src"] = self.pos - vn * 4,
|
||||
["Dir"] = vn * 8,
|
||||
["Spread"] = vector_origin,
|
||||
["IgnoreEntity"] = self.owner,
|
||||
["Attacker"] = self.owner,
|
||||
["Distance"] = 8,
|
||||
["Tracer"] = 0
|
||||
}
|
||||
|
||||
setmetatable(bul, {
|
||||
["__index"] = self.bul
|
||||
})
|
||||
|
||||
self.owner:FireBullets(bul)
|
||||
end
|
||||
|
||||
--Render
|
||||
--local cv_bullet_style, cv_tracers_adv
|
||||
local cv_bullet_style
|
||||
|
||||
if CLIENT then
|
||||
CreateClientConVar("cl_tfa_ballistics_mp", "1", true, false, "Receive bullet data from other players?")
|
||||
cv_bullet_style = CreateClientConVar("cl_tfa_ballistics_fx_bullet", "1", true, false, "Display bullet models for each TFA ballistics bullet?")
|
||||
CreateClientConVar("cl_tfa_ballistics_fx_tracers_style", "1", true, false, "Style of tracers for TFA ballistics? 0=disable,1=smoke")
|
||||
CreateClientConVar("cl_tfa_ballistics_fx_tracers_mp", "1", true, false, "Enable tracers for other TFA ballistics users?")
|
||||
--cv_tracers_adv = CreateClientConVar("cl_tfa_ballistics_fx_tracers_adv", "1", true, false, "Enable advanced tracer calculations for other users? This corrects smoke trail to their barrel")
|
||||
--[[
|
||||
cv_receive = GetConVar("cl_tfa_ballistics_mp")
|
||||
cv_bullet_style = GetConVar("cl_tfa_ballistics_fx_bullet")
|
||||
cv_tracers_style = GetConVar("cl_tfa_ballistics_fx_tracers_style")
|
||||
cv_tracers_mp = GetConVar("cl_tfa_ballistics_fx_tracers_mp")
|
||||
cv_tracers_adv = GetConVar("cl_tfa_ballistics_fx_tracers_adv")
|
||||
]]
|
||||
--
|
||||
end
|
||||
|
||||
--[[local DEFANGPOS = {
|
||||
Pos = vector_origin,
|
||||
Ang = angle_zero
|
||||
}]]
|
||||
|
||||
function BallisticBullet:Render()
|
||||
if SERVER then return end
|
||||
if self.delete then return end
|
||||
|
||||
if not self.curmodel then
|
||||
self.curmodel = ClientsideModel(self.model, RENDERGROUP_OPAQUE)
|
||||
self.curmodel:SetNoDraw(not cv_bullet_style:GetBool())
|
||||
end
|
||||
|
||||
--[==[if IsValid(self.curmodel) and (cv_bullet_style:GetBool() or self.smokeparticle ~= "") then
|
||||
if self.customPosition then
|
||||
fpos = self.pos
|
||||
--fang = self.velocity:Angle()
|
||||
else
|
||||
if self.owner == GetViewEntity() or self.owner == LocalPlayer() then
|
||||
local spos, sang = self.pos, self.velocity:Angle()
|
||||
self.curmodel:SetPos(spos)
|
||||
self.curmodel:SetAngles(sang)
|
||||
|
||||
if not self.vOffsetPos then
|
||||
local att
|
||||
|
||||
if self.inflictor.GetMuzzleAttachment and self.inflictor:GetMuzzleAttachment() then
|
||||
att = self.inflictor:GetMuzzleAttachment()
|
||||
else
|
||||
att = self.inflictor.MuzzleAttachmentRaw or 1
|
||||
end
|
||||
|
||||
if LocalPlayer():ShouldDrawLocalPlayer() then
|
||||
local npos = LocalPlayer():GetActiveWeapon():GetAttachment(att) or DEFANGPOS
|
||||
self.vOffsetPos = self.curmodel:WorldToLocal(npos.Pos)
|
||||
self.vOffsetAng = self.curmodel:WorldToLocalAngles(npos.Ang)
|
||||
else
|
||||
local npos = LocalPlayer():GetViewModel():GetAttachment(att) or DEFANGPOS
|
||||
self.vOffsetPos = self.curmodel:WorldToLocal(npos.Pos)
|
||||
self.vOffsetAng = self.curmodel:WorldToLocalAngles(npos.Ang)
|
||||
end
|
||||
end
|
||||
|
||||
fpos = self.curmodel:LocalToWorld(self.vOffsetPos)
|
||||
--fang = self.curmodel:LocalToWorldAngles(self.vOffsetAng)
|
||||
elseif self.owner:IsPlayer() and cv_tracers_adv:GetBool() then
|
||||
local spos, sang = self.pos, self.velocity:Angle()
|
||||
self.curmodel:SetPos(spos)
|
||||
self.curmodel:SetAngles(sang)
|
||||
|
||||
if not self.vOffsetPos then
|
||||
local npos = self.owner:GetActiveWeapon():GetAttachment(1) or DEFANGPOS
|
||||
self.vOffsetPos = self.curmodel:WorldToLocal(npos.Pos)
|
||||
self.vOffsetAng = self.curmodel:WorldToLocalAngles(npos.Ang)
|
||||
end
|
||||
|
||||
fpos = self.curmodel:LocalToWorld(self.vOffsetPos)
|
||||
--fang = self.curmodel:LocalToWorldAngles(self.vOffsetAng)
|
||||
else
|
||||
fpos = self.pos
|
||||
--fang = self.velocity:Angle()
|
||||
end
|
||||
end
|
||||
|
||||
--[[if cv_bullet_style:GetBool() then
|
||||
self.curmodel:SetupBones()
|
||||
self.curmodel:DrawModel()
|
||||
end]]
|
||||
end]==]
|
||||
|
||||
local fpos, fang = self.pos, self.velocity:Angle()
|
||||
|
||||
self.curmodel:SetPos(fpos)
|
||||
self.curmodel:SetAngles(fang)
|
||||
|
||||
if self.smokeparticle ~= "" and not self.cursmoke then
|
||||
self.cursmoke = CreateParticleSystem(self.curmodel, self.smokeparticle, PATTACH_ABSORIGIN_FOLLOW, 1)
|
||||
if not self.cursmoke then return end
|
||||
self.cursmoke:StartEmission()
|
||||
elseif self.cursmoke and IsValid(self.owner) then
|
||||
self.cursmoke:SetSortOrigin(self.owner.GetShootPos and self.owner:GetShootPos() or self.owner.EyePos and self.owner:EyePos() or vector_origin)
|
||||
|
||||
if self.Underwater then
|
||||
self.cursmoke:StopEmission()
|
||||
self.cursmoke = nil
|
||||
self.smokeparticle = ""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BallisticBullet:Remove()
|
||||
if self.cursmoke then
|
||||
self.cursmoke:StopEmission()
|
||||
self.cursmoke = nil
|
||||
end
|
||||
|
||||
if self.curmodel and self.curmodel.Remove then
|
||||
self.curmodel:Remove()
|
||||
self.curmodel = nil
|
||||
end
|
||||
|
||||
self.delete = true
|
||||
end
|
||||
|
||||
local CopyTable = table.Copy
|
||||
|
||||
function TFA.Ballistics:Bullet(t)
|
||||
local b = CopyTable(t or {})
|
||||
|
||||
setmetatable(b, {
|
||||
["__index"] = BallisticBullet
|
||||
})
|
||||
|
||||
return b
|
||||
end
|
||||
Reference in New Issue
Block a user