mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
1352 lines
38 KiB
Lua
1352 lines
38 KiB
Lua
--[[
|
|
| 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.
|
|
|
|
--[[Thanks to Clavus. Like seriously, SCK was brilliant. Even though you didn't include a license anywhere I could find, it's only fit to credit you.]]
|
|
--
|
|
|
|
local vector_origin = Vector()
|
|
|
|
--[[
|
|
Function Name: InitMods
|
|
Syntax: self:InitMods(). Should be called only once for best performance.
|
|
Returns: Nothing.
|
|
Notes: Creates the VElements and WElements table, and sets up mods.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
function SWEP:InitMods()
|
|
--Create a new table for every weapon instance.
|
|
self.SWEPConstructionKit = true
|
|
|
|
self.ViewModelElements = self:CPTbl(self.ViewModelElements)
|
|
self.WorldModelElements = self:CPTbl(self.WorldModelElements)
|
|
self.ViewModelBoneMods = self:CPTbl(self.ViewModelBoneMods)
|
|
|
|
-- i have no idea how this gonna behave without that with SWEP Construction kit
|
|
-- so we gonna leave this thing alone and precache everything
|
|
self:CreateModels(self.ViewModelElements, true) -- create viewmodels
|
|
self:CreateModels(self.WorldModelElements) -- create worldmodels
|
|
|
|
--Build the bones and such.
|
|
if self:OwnerIsValid() then
|
|
local vm = self:GetOwner():GetViewModel()
|
|
|
|
if IsValid(vm) then
|
|
--self:ResetBonePositions(vm)
|
|
if (self.ShowViewModel == nil or self.ShowViewModel) then
|
|
vm:SetColor(Color(255, 255, 255, 255))
|
|
--This hides the viewmodel, FYI, lol.
|
|
else
|
|
vm:SetMaterial("Debug/hsv")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Function Name: UpdateProjectedTextures
|
|
Syntax: self:UpdateProjectedTextures(). Automatically called already.
|
|
Returns: Nothing.
|
|
Notes: This takes care of our flashlight and laser.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
|
|
function SWEP:UpdateProjectedTextures(view)
|
|
self:DrawLaser(view)
|
|
self:DrawFlashlight(view)
|
|
end
|
|
|
|
--[[
|
|
Function Name: ViewModelDrawn
|
|
Syntax: self:ViewModelDrawn(). Automatically called already.
|
|
Returns: Nothing.
|
|
Notes: This draws the mods.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
function SWEP:PreDrawViewModel(vm, wep, ply)
|
|
self:ProcessBodygroups()
|
|
|
|
--vm:SetupBones()
|
|
|
|
if self:GetHidden() then
|
|
render.SetBlend(0)
|
|
end
|
|
end
|
|
|
|
SWEP.CameraAttachmentOffsets = {{"p", 0}, {"y", 0}, {"r", 0}}
|
|
SWEP.CameraAttachment = nil
|
|
SWEP.CameraAttachments = {"camera", "attach_camera", "view", "cam", "look"}
|
|
SWEP.CameraAngCache = nil
|
|
local tmpvec = Vector(0, 0, -2000)
|
|
|
|
do
|
|
local reference_table
|
|
|
|
local function rendersorter(a, b)
|
|
local ar, br = reference_table[a], reference_table[b]
|
|
|
|
if ar == br then
|
|
return a > b
|
|
end
|
|
|
|
return ar > br
|
|
end
|
|
|
|
local function inc_references(lookup, name, entry, output, level)
|
|
output[name] = (output[name] or 0) + level
|
|
local elemother = lookup[entry.rel]
|
|
|
|
if elemother then
|
|
inc_references(lookup, entry.rel, elemother, output, level + 1)
|
|
end
|
|
end
|
|
|
|
function SWEP:RebuildModsRenderOrder()
|
|
self.vRenderOrder = {}
|
|
self.wRenderOrder = {}
|
|
self.VElementsBodygroupsCache = {}
|
|
self.WElementsBodygroupsCache = {}
|
|
|
|
local ViewModelElements = self:GetStatRaw("ViewModelElements", TFA.LatestDataVersion)
|
|
local WorldModelElements = self:GetStatRaw("WorldModelElements", TFA.LatestDataVersion)
|
|
|
|
if istable(ViewModelElements) then
|
|
local target = self.vRenderOrder
|
|
reference_table = {}
|
|
|
|
for k, v in pairs(ViewModelElements) do
|
|
if v.type == "Model" then
|
|
table.insert(target, k)
|
|
inc_references(ViewModelElements, k, v, reference_table, 10000)
|
|
elseif v.type == "Sprite" or v.type == "Quad" or v.type == "Bodygroup" then
|
|
table.insert(target, k)
|
|
inc_references(ViewModelElements, k, v, reference_table, 1)
|
|
end
|
|
end
|
|
|
|
table.sort(target, rendersorter)
|
|
end
|
|
|
|
if istable(WorldModelElements) then
|
|
local target2 = self.wRenderOrder
|
|
reference_table = {}
|
|
|
|
for k, v in pairs(WorldModelElements) do
|
|
if v.type == "Model" then
|
|
table.insert(target2, 1, k)
|
|
inc_references(WorldModelElements, k, v, reference_table, 10000)
|
|
elseif v.type == "Sprite" or v.type == "Quad" or v.type == "Bodygroup" then
|
|
table.insert(target2, k)
|
|
inc_references(WorldModelElements, k, v, reference_table, 1)
|
|
end
|
|
end
|
|
|
|
table.sort(target2, rendersorter)
|
|
end
|
|
|
|
return self.vRenderOrder, self.wRenderOrder
|
|
end
|
|
end
|
|
|
|
function SWEP:RemoveModsRenderOrder()
|
|
self.vRenderOrder = nil
|
|
end
|
|
|
|
local drawfn, drawself, fndrawpos, fndrawang, fndrawsize
|
|
|
|
local function dodrawfn()
|
|
drawfn(drawself, fndrawpos, fndrawang, fndrawsize)
|
|
end
|
|
|
|
local next_setup_bones = 0
|
|
|
|
function TFA._IncNextSetupBones()
|
|
next_setup_bones = next_setup_bones + 1
|
|
end
|
|
|
|
function TFA._GetNextSetupBones()
|
|
return next_setup_bones
|
|
end
|
|
|
|
local mirror_scale = Vector(1, -1, 1)
|
|
local normal_scale = Vector(1, 1, 1)
|
|
|
|
local mirror = Matrix()
|
|
|
|
local DRAW_AND_SETUP = 0
|
|
local ONLY_DRAW = 1
|
|
local ONLY_SETUP = 2
|
|
|
|
local function draw_element_closure(self, self2, name, index, vm, ViewModelElements, element, nodraw)
|
|
if self2.GetStatL(self, "ViewModelElements." .. name .. ".active") == false then return end
|
|
|
|
local pos, ang = self:GetBoneOrientation(ViewModelElements, element, vm, nil, true)
|
|
if not pos and not element.bonemerge then return end
|
|
|
|
self:PrecacheElement(element, true)
|
|
|
|
local model = element.curmodel
|
|
local sprite = element.spritemat
|
|
|
|
local dodraw = nodraw == DRAW_AND_SETUP or nodraw == ONLY_DRAW
|
|
local dosetup = nodraw == DRAW_AND_SETUP or nodraw == ONLY_SETUP
|
|
|
|
if element.type == "Model" and IsValid(model) then
|
|
if not element.bonemerge and dosetup then
|
|
mirror:Identity()
|
|
|
|
if self2.ViewModelFlip then
|
|
model:SetPos(pos + ang:Forward() * element.pos.x - ang:Right() * element.pos.y + ang:Up() * element.pos.z)
|
|
|
|
ang:RotateAroundAxis(ang:Up(), -element.angle.y)
|
|
ang:RotateAroundAxis(ang:Right(), element.angle.p)
|
|
ang:RotateAroundAxis(ang:Forward(), -element.angle.r)
|
|
|
|
mirror:Scale(mirror_scale)
|
|
mirror:Scale(element.size)
|
|
else
|
|
model:SetPos(pos + ang:Forward() * element.pos.x + ang:Right() * element.pos.y + ang:Up() * element.pos.z)
|
|
|
|
ang:RotateAroundAxis(ang:Up(), element.angle.y)
|
|
ang:RotateAroundAxis(ang:Right(), element.angle.p)
|
|
ang:RotateAroundAxis(ang:Forward(), element.angle.r)
|
|
|
|
mirror:Scale(normal_scale)
|
|
mirror:Scale(element.size)
|
|
end
|
|
|
|
model:SetAngles(ang)
|
|
model:EnableMatrix("RenderMultiply", mirror)
|
|
end
|
|
|
|
if dodraw then
|
|
if element.surpresslightning then
|
|
render.SuppressEngineLighting(true)
|
|
end
|
|
|
|
local material = self:GetStatL("ViewModelElements." .. name .. ".material")
|
|
|
|
if not material or material == "" then
|
|
model:SetMaterial("")
|
|
elseif model:GetMaterial() ~= material then
|
|
model:SetMaterial(material)
|
|
end
|
|
|
|
local skin = self:GetStatL("ViewModelElements." .. name .. ".skin")
|
|
|
|
if skin and skin ~= model:GetSkin() then
|
|
model:SetSkin(skin)
|
|
end
|
|
|
|
if not self2.SCKMaterialCached_V[name] then
|
|
self2.SCKMaterialCached_V[name] = true
|
|
|
|
local materialtable = self:GetStatL("ViewModelElements." .. name .. ".materials", {})
|
|
local entmats = table.GetKeys(model:GetMaterials())
|
|
|
|
for _, k in ipairs(entmats) do
|
|
model:SetSubMaterial(k - 1, materialtable[k] or "")
|
|
end
|
|
end
|
|
end
|
|
|
|
if dosetup then
|
|
if not self2.VElementsBodygroupsCache[index] then
|
|
self2.VElementsBodygroupsCache[index] = #model:GetBodyGroups() - 1
|
|
end
|
|
|
|
if self2.VElementsBodygroupsCache[index] then
|
|
for _b = 0, self2.VElementsBodygroupsCache[index] do
|
|
local newbg = self2.GetStatL(self, "ViewModelElements." .. name .. ".bodygroup." .. _b, 0) -- names are not supported, use overridetable
|
|
|
|
if model:GetBodygroup(_b) ~= newbg then
|
|
model:SetBodygroup(_b, newbg)
|
|
end
|
|
end
|
|
end
|
|
|
|
if element.bonemerge then
|
|
model:SetPos(pos)
|
|
model:SetAngles(ang)
|
|
|
|
if element.rel and ViewModelElements[element.rel] and IsValid(ViewModelElements[element.rel].curmodel) then
|
|
element.parModel = ViewModelElements[element.rel].curmodel
|
|
else
|
|
element.parModel = self2.OwnerViewModel or self
|
|
end
|
|
|
|
if model:GetParent() ~= element.parModel then
|
|
model:SetParent(element.parModel)
|
|
end
|
|
|
|
if not model:IsEffectActive(EF_BONEMERGE) then
|
|
model:AddEffects(EF_BONEMERGE)
|
|
model:AddEffects(EF_BONEMERGE_FASTCULL)
|
|
model:SetMoveType(MOVETYPE_NONE)
|
|
model:SetLocalPos(vector_origin)
|
|
model:SetLocalAngles(angle_zero)
|
|
end
|
|
elseif model:IsEffectActive(EF_BONEMERGE) then
|
|
model:RemoveEffects(EF_BONEMERGE)
|
|
model:SetParent(NULL)
|
|
end
|
|
end
|
|
|
|
if dodraw then
|
|
render.SetColorModulation(element.color.r / 255, element.color.g / 255, element.color.b / 255)
|
|
render.SetBlend(element.color.a / 255)
|
|
end
|
|
|
|
if dosetup and model.tfa_next_setup_bones ~= next_setup_bones then
|
|
model:InvalidateBoneCache()
|
|
model:SetupBones()
|
|
model.tfa_next_setup_bones = next_setup_bones
|
|
end
|
|
|
|
if dodraw then
|
|
if self2.ViewModelFlip then
|
|
render.CullMode(MATERIAL_CULLMODE_CW)
|
|
end
|
|
|
|
model:DrawModel()
|
|
|
|
render.SetBlend(1)
|
|
render.SetColorModulation(1, 1, 1)
|
|
|
|
if self2.ViewModelFlip then
|
|
render.CullMode(MATERIAL_CULLMODE_CCW)
|
|
end
|
|
|
|
if element.surpresslightning then
|
|
render.SuppressEngineLighting(false)
|
|
end
|
|
end
|
|
elseif dodraw and element.type == "Sprite" and sprite then
|
|
local drawpos = pos + ang:Forward() * element.pos.x + ang:Right() * element.pos.y + ang:Up() * element.pos.z
|
|
render.SetMaterial(sprite)
|
|
render.DrawSprite(drawpos, element.size.x, element.size.y, element.color)
|
|
elseif dodraw and element.type == "Quad" and element.draw_func then
|
|
local drawpos = pos + ang:Forward() * element.pos.x + ang:Right() * element.pos.y + ang:Up() * element.pos.z
|
|
ang:RotateAroundAxis(ang:Up(), element.angle.y)
|
|
ang:RotateAroundAxis(ang:Right(), element.angle.p)
|
|
ang:RotateAroundAxis(ang:Forward(), element.angle.r)
|
|
|
|
cam.Start3D2D(drawpos, ang, element.size)
|
|
render.PushFilterMin(TEXFILTER.ANISOTROPIC)
|
|
render.PushFilterMag(TEXFILTER.ANISOTROPIC)
|
|
|
|
drawfn, drawself, fndrawpos, fndrawang, fndrawsize = element.draw_func, self, nil, nil, nil
|
|
ProtectedCall(dodrawfn)
|
|
|
|
render.PopFilterMin()
|
|
render.PopFilterMag()
|
|
cam.End3D2D()
|
|
end
|
|
end
|
|
|
|
function SWEP:ViewModelDrawn()
|
|
local self2 = self:GetTable()
|
|
render.SetBlend(1)
|
|
|
|
if self2.DrawHands then
|
|
self2.DrawHands(self)
|
|
end
|
|
|
|
local vm = self.OwnerViewModel
|
|
if not IsValid(vm) then return end
|
|
if not self:GetOwner().GetHands then return end
|
|
|
|
if self2.UseHands then
|
|
local hands = self:GetOwner():GetHands()
|
|
|
|
if IsValid(hands) then
|
|
if not self2.GetHidden(self) then
|
|
hands:SetParent(vm)
|
|
else
|
|
hands:SetParent(nil)
|
|
hands:SetPos(tmpvec)
|
|
end
|
|
end
|
|
end
|
|
|
|
self2.UpdateBonePositions(self, vm)
|
|
|
|
if not self2.CameraAttachment then
|
|
self2.CameraAttachment = -1
|
|
|
|
for _, v in ipairs(self2.CameraAttachments) do
|
|
local attid = vm:LookupAttachment(v)
|
|
|
|
if attid and attid > 0 then
|
|
self2.CameraAttachment = attid
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if self2.CameraAttachment and self2.CameraAttachment > 0 then
|
|
local angpos = vm:GetAttachment(self2.CameraAttachment)
|
|
|
|
if angpos and angpos.Ang then
|
|
local angv = angpos.Ang
|
|
local off = vm:WorldToLocalAngles(angv)
|
|
local spd = 15
|
|
local cycl = vm:GetCycle()
|
|
local dissipatestart = 0
|
|
self2.CameraAngCache = self2.CameraAngCache or off
|
|
|
|
for _, v in pairs(self2.CameraAttachmentOffsets) do
|
|
local offtype = v[1]
|
|
local offang = v[2]
|
|
|
|
if offtype == "p" then
|
|
off:RotateAroundAxis(off:Right(), offang)
|
|
elseif offtype == "y" then
|
|
off:RotateAroundAxis(off:Up(), offang)
|
|
elseif offtype == "r" then
|
|
off:RotateAroundAxis(off:Forward(), offang)
|
|
end
|
|
end
|
|
|
|
if self2.ViewModelFlip then
|
|
off = Angle()
|
|
end
|
|
|
|
local actind = vm:GetSequenceActivity(vm:GetSequence())
|
|
|
|
if (actind == ACT_VM_DRAW or actind == ACT_VM_HOLSTER_EMPTY or actind == ACT_VM_DRAW_SILENCED) and vm:GetCycle() < 0.05 then
|
|
self2.CameraAngCache.p = 0
|
|
self2.CameraAngCache.y = 0
|
|
self2.CameraAngCache.r = 0
|
|
end
|
|
|
|
if (actind == ACT_VM_HOLSTER or actind == ACT_VM_HOLSTER_EMPTY) and cycl > dissipatestart then
|
|
self2.CameraAngCache.p = self2.CameraAngCache.p * (1 - cycl) / (1 - dissipatestart)
|
|
self2.CameraAngCache.y = self2.CameraAngCache.y * (1 - cycl) / (1 - dissipatestart)
|
|
self2.CameraAngCache.r = self2.CameraAngCache.r * (1 - cycl) / (1 - dissipatestart)
|
|
end
|
|
|
|
self2.CameraAngCache.p = math.ApproachAngle(self2.CameraAngCache.p, off.p, (self2.CameraAngCache.p - off.p) * FrameTime() * spd)
|
|
self2.CameraAngCache.y = math.ApproachAngle(self2.CameraAngCache.y, off.y, (self2.CameraAngCache.y - off.y) * FrameTime() * spd)
|
|
self2.CameraAngCache.r = math.ApproachAngle(self2.CameraAngCache.r, off.r, (self2.CameraAngCache.r - off.r) * FrameTime() * spd)
|
|
else
|
|
self2.CameraAngCache.p = 0
|
|
self2.CameraAngCache.y = 0
|
|
self2.CameraAngCache.r = 0
|
|
end
|
|
end
|
|
|
|
local ViewModelElements = self:GetStatRawL("ViewModelElements")
|
|
local ViewModelBodygroups = self:GetStatRawL("ViewModelBodygroups")
|
|
|
|
if ViewModelElements and self2.HasInitAttachments then
|
|
-- ViewModelElements = self:GetStatL("ViewModelElements")
|
|
-- self:CreateModels(ViewModelElements, true)
|
|
|
|
self2.SCKMaterialCached_V = self2.SCKMaterialCached_V or {}
|
|
|
|
if not self2.vRenderOrder then
|
|
self:RebuildModsRenderOrder()
|
|
end
|
|
|
|
vm:InvalidateBoneCache()
|
|
vm:SetupBones()
|
|
next_setup_bones = next_setup_bones + 1
|
|
|
|
for index = 1, #self2.vRenderOrder do
|
|
local name = self2.vRenderOrder[index]
|
|
local element = ViewModelElements[name]
|
|
|
|
if not element then
|
|
self:RebuildModsRenderOrder()
|
|
break
|
|
end
|
|
|
|
if element.type == "Bodygroup" then
|
|
if element.index and element.value_active then
|
|
ViewModelBodygroups[element.index] = self2.GetStatL(self, "ViewModelElements." .. name .. ".active") and element.value_active or (element.value_inactive or 0)
|
|
end
|
|
|
|
goto CONTINUE
|
|
end
|
|
|
|
if element.hide then goto CONTINUE end
|
|
|
|
if element.type == "Quad" and element.draw_func_outer then goto CONTINUE end
|
|
if not element.bone and not element.attachment then goto CONTINUE end
|
|
|
|
draw_element_closure(self, self2, name, index, vm, ViewModelElements, element, element.translucent == true and ONLY_SETUP or DRAW_AND_SETUP)
|
|
|
|
::CONTINUE::
|
|
end
|
|
end
|
|
|
|
if not self2.UseHands and self2.ViewModelDrawnPost then
|
|
self:ViewModelDrawnPost()
|
|
self:ViewModelDrawnPostFinal()
|
|
end
|
|
|
|
if self2.ShellEjectionQueue ~= 0 then
|
|
for i = 1, self2.ShellEjectionQueue do
|
|
self:MakeShell(true)
|
|
end
|
|
|
|
self2.ShellEjectionQueue = 0
|
|
end
|
|
end
|
|
|
|
function SWEP:ViewModelDrawnPostFinal()
|
|
local self2 = self:GetTable()
|
|
local vm = self.OwnerViewModel
|
|
if not IsValid(vm) then return end
|
|
|
|
local ViewModelElements = self:GetStatRawL("ViewModelElements")
|
|
if not ViewModelElements then return end
|
|
|
|
for index = 1, #self2.vRenderOrder do
|
|
local name = self2.vRenderOrder[index]
|
|
local element = ViewModelElements[name]
|
|
|
|
if element.hide or not element.translucent then goto CONTINUE end
|
|
|
|
if element.type == "Quad" and element.draw_func_outer then goto CONTINUE end
|
|
if not element.bone and not element.attachment then goto CONTINUE end
|
|
|
|
draw_element_closure(self, self2, name, index, vm, ViewModelElements, element, ONLY_DRAW)
|
|
|
|
::CONTINUE::
|
|
end
|
|
end
|
|
|
|
function SWEP:ViewModelDrawnPost()
|
|
local self2 = self:GetTable()
|
|
if not self2.VMIV(self) then return end
|
|
|
|
if not self.ViewModelFlip then
|
|
self2.CacheSightsPos(self, self.OwnerViewModel, false)
|
|
end
|
|
|
|
local ViewModelElements = self:GetStatRaw("ViewModelElements", TFA.LatestDataVersion)
|
|
|
|
if not ViewModelElements or not self2.vRenderOrder then return end
|
|
|
|
for index = 1, #self2.vRenderOrder do
|
|
local name = self2.vRenderOrder[index]
|
|
local element = ViewModelElements[name]
|
|
|
|
if element.type == "Quad" and element.draw_func_outer and not element.hide and (element.bone or element.attachment and element.attachment ~= "") and self:GetStatL("ViewModelElements." .. name .. ".active") ~= false then
|
|
local pos, ang = self:GetBoneOrientation(ViewModelElements, element, self2.OwnerViewModel)
|
|
|
|
if pos then
|
|
local drawpos = pos + ang:Forward() * element.pos.x + ang:Right() * element.pos.y + ang:Up() * element.pos.z
|
|
|
|
ang:RotateAroundAxis(ang:Up(), element.angle.y)
|
|
ang:RotateAroundAxis(ang:Right(), element.angle.p)
|
|
ang:RotateAroundAxis(ang:Forward(), element.angle.r)
|
|
|
|
drawfn, drawself, fndrawpos, fndrawang, fndrawsize = element.draw_func_outer, self, drawpos, ang, element.size
|
|
ProtectedCall(dodrawfn)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Function Name: DrawWorldModel
|
|
Syntax: self:DrawWorldModel(). Automatically called already.
|
|
Returns: Nothing.
|
|
Notes: This draws the world model, plus its attachments.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
function SWEP:DrawWorldModel()
|
|
local self2 = self:GetTable()
|
|
|
|
local skinStat = self2.GetStatL(self, "Skin")
|
|
if isnumber(skinStat) then
|
|
if self:GetSkin() ~= skinStat then
|
|
self:SetSkin(skinStat)
|
|
end
|
|
end
|
|
|
|
if not self2.MaterialCached_W and self2.GetStatL(self, "MaterialTable_W") then
|
|
self2.MaterialCached_W = {}
|
|
self:SetSubMaterial()
|
|
|
|
local collectedKeys = table.GetKeys(self2.GetStatL(self, "MaterialTable_W"))
|
|
table.Merge(collectedKeys, table.GetKeys(self2.GetStatL(self, "MaterialTable")))
|
|
|
|
for _, k in ipairs(collectedKeys) do
|
|
if (k == "BaseClass") then goto CONTINUE end
|
|
|
|
local v = self2.GetStatL(self, "MaterialTable_W")[k]
|
|
|
|
if not self2.MaterialCached_W[k] then
|
|
self:SetSubMaterial(k - 1, v)
|
|
self2.MaterialCached_W[k] = true
|
|
end
|
|
|
|
::CONTINUE::
|
|
end
|
|
end
|
|
|
|
local ply = self:GetOwner()
|
|
local validowner = IsValid(ply)
|
|
|
|
if validowner then
|
|
-- why? this tanks FPS because source doesn't have a chance to setup bones when it needs to
|
|
-- instead we ask it to do it `right now`
|
|
-- k then
|
|
ply:SetupBones()
|
|
ply:InvalidateBoneCache()
|
|
self:InvalidateBoneCache()
|
|
end
|
|
|
|
if self2.ShowWorldModel == nil or self2.ShowWorldModel or not validowner then
|
|
self2.WorldModelOffsetUpdate(self, ply)
|
|
self2.ProcessBodygroups(self)
|
|
|
|
self:DrawModel()
|
|
end
|
|
|
|
self:SetupBones()
|
|
self2.UpdateWMBonePositions(self)
|
|
|
|
self:DrawWElements()
|
|
|
|
if IsValid(self) and self.IsTFAWeapon and (self:GetOwner() ~= LocalPlayer() or not self:IsFirstPerson()) then
|
|
self2.UpdateProjectedTextures(self, false)
|
|
end
|
|
end
|
|
|
|
function SWEP:DrawWElements()
|
|
local self2 = self:GetTable()
|
|
|
|
local WorldModelElements = self2.GetStatRaw(self, "WorldModelElements", TFA.LatestDataVersion)
|
|
|
|
if not WorldModelElements then return end
|
|
|
|
if not self2.SCKMaterialCached_W then
|
|
self2.SCKMaterialCached_W = {}
|
|
end
|
|
|
|
if not self2.wRenderOrder then
|
|
self2.RebuildModsRenderOrder(self)
|
|
end
|
|
|
|
local ply = self:GetOwner()
|
|
local validowner = IsValid(ply)
|
|
|
|
for index = 1, #self2.wRenderOrder do
|
|
local name = self2.wRenderOrder[index]
|
|
local element = WorldModelElements[name]
|
|
|
|
if not element then
|
|
self2.RebuildModsRenderOrder(self)
|
|
break
|
|
end
|
|
|
|
if element.type == "Bodygroup" then
|
|
if element.index and element.value_active then
|
|
self2.WorldModelBodygroups[element.index] = self2.GetStatL(self, "WorldModelElements." .. name .. ".active") and element.value_active or (element.value_inactive or 0)
|
|
end
|
|
|
|
goto CONTINUE
|
|
end
|
|
|
|
if element.hide then goto CONTINUE end
|
|
if self2.GetStatL(self, "WorldModelElements." .. name .. ".active") == false then goto CONTINUE end
|
|
|
|
local bone_ent = (validowner and ply:LookupBone(element.bone or "ValveBiped.Bip01_R_Hand")) and ply or self
|
|
local pos, ang
|
|
|
|
if element.bone then
|
|
pos, ang = self2.GetBoneOrientation(self, WorldModelElements, element, bone_ent)
|
|
else
|
|
pos, ang = self2.GetBoneOrientation(self, WorldModelElements, element, bone_ent, "ValveBiped.Bip01_R_Hand")
|
|
end
|
|
|
|
if not pos and not element.bonemerge then goto CONTINUE end
|
|
|
|
self2.PrecacheElement(self, element, true)
|
|
|
|
local model = element.curmodel
|
|
local sprite = element.spritemat
|
|
|
|
if element.type == "Model" and IsValid(model) then
|
|
if element.bonemerge then
|
|
model:SetPos(pos)
|
|
model:SetAngles(ang)
|
|
else
|
|
model:SetPos(pos + ang:Forward() * element.pos.x + ang:Right() * element.pos.y + ang:Up() * element.pos.z)
|
|
|
|
ang:RotateAroundAxis(ang:Up(), element.angle.y)
|
|
ang:RotateAroundAxis(ang:Right(), element.angle.p)
|
|
ang:RotateAroundAxis(ang:Forward(), element.angle.r)
|
|
|
|
model:SetAngles(ang)
|
|
end
|
|
|
|
local material = self2.GetStatL(self, "WorldModelElements." .. name .. ".material")
|
|
|
|
if not material or material == "" then
|
|
model:SetMaterial("")
|
|
elseif model:GetMaterial() ~= material then
|
|
model:SetMaterial(material)
|
|
end
|
|
|
|
local skin = self2.GetStatL(self, "WorldModelElements." .. name .. ".skin")
|
|
|
|
if skin and skin ~= model:GetSkin() then
|
|
model:SetSkin(skin)
|
|
end
|
|
|
|
if not self2.SCKMaterialCached_W[name] then
|
|
self2.SCKMaterialCached_W[name] = true
|
|
|
|
local materialtable = self2.GetStatL(self, "WorldModelElements." .. name .. ".materials", {})
|
|
local entmats = table.GetKeys(model:GetMaterials())
|
|
|
|
for _, k in ipairs(entmats) do
|
|
model:SetSubMaterial(k - 1, materialtable[k] or "")
|
|
end
|
|
end
|
|
|
|
if not self2.WElementsBodygroupsCache[index] then
|
|
self2.WElementsBodygroupsCache[index] = #model:GetBodyGroups() - 1
|
|
end
|
|
|
|
if self2.WElementsBodygroupsCache[index] then
|
|
for _b = 0, self2.WElementsBodygroupsCache[index] do
|
|
local newbg = self2.GetStatL(self, "WorldModelElements." .. name .. ".bodygroup." .. _b, 0) -- names are not supported, use overridetable
|
|
|
|
if model:GetBodygroup(_b) ~= newbg then
|
|
model:SetBodygroup(_b, newbg)
|
|
end
|
|
end
|
|
end
|
|
|
|
if element.surpresslightning then
|
|
render.SuppressEngineLighting(true)
|
|
end
|
|
|
|
if element.bonemerge then
|
|
if element.rel and WorldModelElements[element.rel] and IsValid(WorldModelElements[element.rel].curmodel) and WorldModelElements[element.rel].bone ~= "oof" then
|
|
element.parModel = WorldModelElements[element.rel].curmodel
|
|
else
|
|
element.parModel = self
|
|
end
|
|
|
|
if model:GetParent() ~= element.parModel then
|
|
model:SetParent(element.parModel)
|
|
end
|
|
|
|
if not model:IsEffectActive(EF_BONEMERGE) then
|
|
model:AddEffects(EF_BONEMERGE)
|
|
model:SetLocalPos(vector_origin)
|
|
model:SetLocalAngles(angle_zero)
|
|
end
|
|
elseif model:IsEffectActive(EF_BONEMERGE) then
|
|
model:RemoveEffects(EF_BONEMERGE)
|
|
model:SetParent(nil)
|
|
end
|
|
|
|
render.SetColorModulation(element.color.r / 255, element.color.g / 255, element.color.b / 255)
|
|
render.SetBlend(element.color.a / 255)
|
|
|
|
model:DrawModel()
|
|
|
|
render.SetBlend(1)
|
|
render.SetColorModulation(1, 1, 1)
|
|
|
|
if element.surpresslightning then
|
|
render.SuppressEngineLighting(false)
|
|
end
|
|
elseif element.type == "Sprite" and sprite then
|
|
local drawpos = pos + ang:Forward() * element.pos.x + ang:Right() * element.pos.y + ang:Up() * element.pos.z
|
|
render.SetMaterial(sprite)
|
|
render.DrawSprite(drawpos, element.size.x, element.size.y, element.color)
|
|
elseif element.type == "Quad" and element.draw_func then
|
|
local drawpos = pos + ang:Forward() * element.pos.x + ang:Right() * element.pos.y + ang:Up() * element.pos.z
|
|
ang:RotateAroundAxis(ang:Up(), element.angle.y)
|
|
ang:RotateAroundAxis(ang:Right(), element.angle.p)
|
|
ang:RotateAroundAxis(ang:Forward(), element.angle.r)
|
|
cam.Start3D2D(drawpos, ang, element.size)
|
|
|
|
drawfn, drawself, fndrawpos, fndrawang, fndrawsize = element.draw_func, self, nil, nil, nil
|
|
ProtectedCall(dodrawfn)
|
|
|
|
cam.End3D2D()
|
|
end
|
|
|
|
::CONTINUE::
|
|
end
|
|
end
|
|
|
|
function SWEP:WorldModelOffsetUpdate(ply)
|
|
if not IsValid(ply) then
|
|
self:SetRenderOrigin(nil)
|
|
self:SetRenderAngles(nil)
|
|
|
|
local WorldModelOffset = self:GetStatRawL("WorldModelOffset")
|
|
|
|
if WorldModelOffset and WorldModelOffset.Scale then
|
|
self:SetModelScale(WorldModelOffset.Scale, 0)
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
|
|
local WorldModelOffset = self:GetStatRawL("WorldModelOffset")
|
|
|
|
-- THIS IS DANGEROUS
|
|
if WorldModelOffset and WorldModelOffset.Pos and WorldModelOffset.Ang then
|
|
-- TO DO ONLY CLIENTSIDE
|
|
-- since this will break hitboxes!
|
|
local handBone = ply:LookupBone("ValveBiped.Bip01_R_Hand")
|
|
|
|
if handBone then
|
|
--local pos, ang = ply:GetBonePosition(handBone)
|
|
local pos, ang
|
|
local mat = ply:GetBoneMatrix(handBone)
|
|
|
|
if mat then
|
|
pos, ang = mat:GetTranslation(), mat:GetAngles()
|
|
else
|
|
pos, ang = ply:GetBonePosition(handBone)
|
|
end
|
|
|
|
local opos, oang, oscale = WorldModelOffset.Pos, WorldModelOffset.Ang, WorldModelOffset.Scale
|
|
|
|
pos = pos + ang:Forward() * opos.Forward + ang:Right() * opos.Right + ang:Up() * opos.Up
|
|
ang:RotateAroundAxis(ang:Up(), oang.Up)
|
|
ang:RotateAroundAxis(ang:Right(), oang.Right)
|
|
ang:RotateAroundAxis(ang:Forward(), oang.Forward)
|
|
self:SetRenderOrigin(pos)
|
|
self:SetRenderAngles(ang)
|
|
--if WorldModelOffset.Scale and ( !self2.MyModelScale or ( WorldModelOffset and self2.MyModelScale!=WorldModelOffset.Scale ) ) then
|
|
self:SetModelScale(oscale or 1, 0)
|
|
--end
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Function Name: GetBoneOrientation
|
|
Syntax: self:GetBoneOrientation( base bone mod table, bone mod table, entity, bone override ).
|
|
Returns: Position, Angle.
|
|
Notes: This is a very specific function for a specific purpose, and shouldn't be used generally to get a bone's orientation.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
function SWEP:GetBoneOrientation(basetabl, tabl, ent, bone_override, isVM, isAttachment, isNonRoot)
|
|
local bone, pos, ang
|
|
|
|
if not IsValid(ent) then return Vector(), Angle() end
|
|
|
|
if not isNonRoot and tabl.rel and tabl.rel ~= "" and not tabl.bonemerge then
|
|
local v = basetabl[tabl.rel]
|
|
if not v then return Vector(), Angle() end
|
|
|
|
local boneName = tabl.bone
|
|
|
|
if tabl.attachment and tabl.attachment ~= "" and v.curmodel:LookupAttachment(tabl.attachment) ~= 0 then
|
|
pos, ang = self:GetBoneOrientation(basetabl, v, v.curmodel, tabl.attachment, isVM, true, true)
|
|
|
|
if pos and ang then return pos, ang end
|
|
elseif v.curmodel and ent ~= v.curmodel and (v.bonemerge or (boneName and boneName ~= "" and v.curmodel:LookupBone(boneName))) then
|
|
pos, ang = self:GetBoneOrientation(basetabl, v, v.curmodel, boneName, isVM, false, true)
|
|
|
|
if pos and ang then return pos, ang end
|
|
else
|
|
--As clavus states in his original code, don't make your elements named the same as a bone, because recursion.
|
|
pos, ang = self:GetBoneOrientation(basetabl, v, ent, nil, isVM, false, true)
|
|
|
|
if pos and ang then
|
|
pos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
|
|
|
|
ang:RotateAroundAxis(ang:Up(), v.angle.y)
|
|
ang:RotateAroundAxis(ang:Right(), v.angle.p)
|
|
ang:RotateAroundAxis(ang:Forward(), v.angle.r)
|
|
|
|
-- For mirrored viewmodels. You might think to scale negatively on X, but this isn't the case.
|
|
|
|
return pos, ang
|
|
end
|
|
end
|
|
end
|
|
|
|
if isAttachment == nil then isAttachment = tabl.attachment ~= nil end
|
|
|
|
if isnumber(bone_override) then
|
|
bone = bone_override
|
|
elseif isAttachment then
|
|
bone = ent:LookupAttachment(bone_override or tabl.attachment)
|
|
else
|
|
bone = ent:LookupBone(bone_override or tabl.bone) or 0
|
|
end
|
|
|
|
if not bone or bone == -1 then return end
|
|
pos, ang = Vector(0, 0, 0), Angle(0, 0, 0)
|
|
|
|
if ent.tfa_next_setup_bones ~= next_setup_bones then
|
|
ent:InvalidateBoneCache()
|
|
ent:SetupBones()
|
|
ent.tfa_next_setup_bones = next_setup_bones
|
|
end
|
|
|
|
if isAttachment then
|
|
-- mmmm yes tasty LuaVM memory
|
|
-- GC screams in agony
|
|
local get = ent:GetAttachment(bone)
|
|
|
|
if get then
|
|
pos, ang = get.Pos, get.Ang
|
|
end
|
|
else
|
|
|
|
local m = ent:GetBoneMatrix(bone)
|
|
|
|
if m then
|
|
pos, ang = m:GetTranslation(), m:GetAngles()
|
|
end
|
|
end
|
|
|
|
local owner = self:GetOwner()
|
|
|
|
if isVM and self.ViewModelFlip then
|
|
ang.r = -ang.r
|
|
end
|
|
|
|
return pos, ang
|
|
end
|
|
--[[
|
|
Function Name: CleanModels
|
|
Syntax: self:CleanModels( elements table ).
|
|
Returns: Nothing.
|
|
Notes: Removes all existing models.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
function SWEP:CleanModels(input)
|
|
if not istable(input) then return end
|
|
|
|
for _, v in pairs(input) do
|
|
if (v.type == "Model" and v.curmodel) then
|
|
if IsValid(v.curmodel) then
|
|
v.curmodel:Remove()
|
|
end
|
|
|
|
v.curmodel = nil
|
|
elseif (v.type == "Sprite" and v.sprite and v.sprite ~= "" and (not v.spritemat or v.cursprite ~= v.sprite)) then
|
|
v.cursprite = nil
|
|
v.spritemat = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
function SWEP:PrecacheElementModel(element, is_vm)
|
|
element.curmodel = ClientsideModel(element.model, RENDERGROUP_OTHER)
|
|
element.curmodel.tfa_gun_parent = self
|
|
element.curmodel.tfa_gun_clmodel = true
|
|
|
|
if self.SWEPConstructionKit then
|
|
TFA.RegisterClientsideModel(element.curmodel, self)
|
|
end
|
|
|
|
if not IsValid(element.curmodel) then
|
|
element.curmodel = nil
|
|
return
|
|
end
|
|
|
|
element.curmodel:SetPos(self:GetPos())
|
|
element.curmodel:SetAngles(self:GetAngles())
|
|
element.curmodel:SetParent(self)
|
|
element.curmodel:SetOwner(self)
|
|
element.curmodel:SetNoDraw(true)
|
|
|
|
if element.material then
|
|
element.curmodel:SetMaterial(element.material or "")
|
|
end
|
|
|
|
if element.skin then
|
|
element.curmodel:SetSkin(element.skin)
|
|
end
|
|
|
|
local matrix = Matrix()
|
|
matrix:Scale(element.size)
|
|
|
|
element.curmodel:EnableMatrix("RenderMultiply", matrix)
|
|
element.curmodelname = element.model
|
|
element.view = is_vm == true
|
|
|
|
-- // make sure we create a unique name based on the selected options
|
|
end
|
|
|
|
do
|
|
local tocheck = {"nocull", "additive", "vertexalpha", "vertexcolor", "ignorez"}
|
|
|
|
function SWEP:PrecacheElementSprite(element, is_vm)
|
|
if element.vmt then
|
|
element.spritemat = Material(element.sprite)
|
|
element.cursprite = element.sprite
|
|
return
|
|
end
|
|
|
|
local name = "tfa-" .. element.sprite .. "-"
|
|
|
|
local params = {
|
|
["$basetexture"] = element.sprite
|
|
}
|
|
|
|
for _, element_property in ipairs(tocheck) do
|
|
if (element[element_property]) then
|
|
params["$" .. element_property] = 1
|
|
name = name .. "1"
|
|
else
|
|
name = name .. "0"
|
|
end
|
|
end
|
|
|
|
element.cursprite = element.sprite
|
|
element.spritemat = CreateMaterial(name, "UnlitGeneric", params)
|
|
end
|
|
end
|
|
|
|
function SWEP:PrecacheElement(element, is_vm)
|
|
if element.type == "Model" and element.model and (not IsValid(element.curmodel) or element.curmodelname ~= element.model) and element.model ~= "" then
|
|
if IsValid(element.curmodel) then
|
|
element.curmodel:Remove()
|
|
end
|
|
|
|
self:PrecacheElementModel(element, is_vm)
|
|
elseif (element.type == "Sprite" and element.sprite and element.sprite ~= "" and (not element.spritemat or element.cursprite ~= element.sprite)) then
|
|
self:PrecacheElementSprite(element, is_vm)
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Function Name: CreateModels
|
|
Syntax: self:CreateModels( elements table ).
|
|
Returns: Nothing.
|
|
Notes: Creates the elements for whatever you give it.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
function SWEP:CreateModels(input, is_vm)
|
|
if not istable(input) then return end
|
|
|
|
for _, element in pairs(input) do
|
|
self:PrecacheElement(element, is_vm)
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Function Name: UpdateBonePositions
|
|
Syntax: self:UpdateBonePositions( viewmodel ).
|
|
Returns: Nothing.
|
|
Notes: Updates the bones for a viewmodel.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
local bpos, bang
|
|
local onevec = Vector(1, 1, 1)
|
|
local getKeys = table.GetKeys
|
|
|
|
local function appendTable(t, t2)
|
|
for i = 1, #t2 do
|
|
t[#t + 1] = t2[i]
|
|
end
|
|
end
|
|
|
|
SWEP.ChildrenScaled = {}
|
|
SWEP.ViewModelBoneMods_Children = {}
|
|
|
|
function SWEP:ScaleChildBoneMods(ent,bone,cumulativeScale)
|
|
if self.ChildrenScaled[bone] then
|
|
return
|
|
end
|
|
self.ChildrenScaled[bone] = true
|
|
local boneid = ent:LookupBone(bone)
|
|
if not boneid then return end
|
|
local curScale = (cumulativeScale or Vector(1,1,1)) * 1
|
|
if self.ViewModelBoneMods[bone] then
|
|
curScale = curScale * self.ViewModelBoneMods[bone].scale
|
|
end
|
|
local ch = ent:GetChildBones(boneid)
|
|
if ch and #ch > 0 then
|
|
for _, boneChild in ipairs(ch) do
|
|
self:ScaleChildBoneMods(ent,ent:GetBoneName(boneChild),curScale)
|
|
end
|
|
end
|
|
if self.ViewModelBoneMods[bone] then
|
|
self.ViewModelBoneMods[bone].scale = curScale
|
|
else
|
|
self.ViewModelBoneMods_Children[bone] = {
|
|
["pos"] = vector_origin,
|
|
["angle"] = angle_zero,
|
|
["scale"] = curScale * 1
|
|
}
|
|
end
|
|
end
|
|
|
|
local vmbm_old_count = 0
|
|
|
|
function SWEP:UpdateBonePositions(vm)
|
|
local self2 = self:GetTable()
|
|
local vmbm = self2.GetStatL(self, "ViewModelBoneMods")
|
|
|
|
local vmbm_count = 0
|
|
|
|
if vmbm then
|
|
vmbm_count = table.Count(vmbm)
|
|
end
|
|
|
|
if vmbm_old_count ~= vmbm_count then
|
|
self:ResetBonePositions()
|
|
end
|
|
|
|
vmbm_old_count = vmbm_count
|
|
|
|
if vmbm then
|
|
local stat = self:GetStatus()
|
|
|
|
if not self2.BlowbackBoneMods then
|
|
self2.BlowbackBoneMods = {}
|
|
self2.BlowbackCurrent = 0
|
|
end
|
|
|
|
if not self2.HasSetMetaVMBM then
|
|
for k,v in pairs(self2.ViewModelBoneMods) do
|
|
if (k == "BaseClass") then goto CONTINUE end -- do not name your bones like this pls
|
|
|
|
local scale = v.scale
|
|
|
|
if scale and scale.x ~= 1 or scale.y ~= 1 or scale.z ~= 1 then
|
|
self:ScaleChildBoneMods(vm, k)
|
|
end
|
|
|
|
::CONTINUE::
|
|
end
|
|
|
|
for _,v in pairs(self2.BlowbackBoneMods) do
|
|
v.pos_og = v.pos
|
|
v.angle_og = v.angle
|
|
v.scale_og = v.scale or onevec
|
|
end
|
|
|
|
self2.HasSetMetaVMBM = true
|
|
self2.ViewModelBoneMods["wepEnt"] = self
|
|
|
|
setmetatable(self2.ViewModelBoneMods, {__index = function(t,k)
|
|
if not IsValid(self) then return end
|
|
if self2.ViewModelBoneMods_Children[k] then return self2.ViewModelBoneMods_Children[k] end
|
|
if not self2.BlowbackBoneMods[k] then return end
|
|
if not ( self2.SequenceEnabled[ACT_VM_RELOAD_EMPTY] and TFA.Enum.ReloadStatus[stat] and self2.Blowback_PistolMode ) then
|
|
self2.BlowbackBoneMods[k].pos = self2.BlowbackBoneMods[k].pos_og * self2.BlowbackCurrent
|
|
self2.BlowbackBoneMods[k].angle = self2.BlowbackBoneMods[k].angle_og * self2.BlowbackCurrent
|
|
self2.BlowbackBoneMods[k].scale = Lerp(self2.BlowbackCurrent, onevec, self2.BlowbackBoneMods[k].scale_og)
|
|
return self2.BlowbackBoneMods[k]
|
|
end
|
|
end})
|
|
end
|
|
|
|
if not ( self2.SequenceEnabled[ACT_VM_RELOAD_EMPTY] and TFA.Enum.ReloadStatus[stat] and self2.Blowback_PistolMode ) then
|
|
self2.BlowbackCurrent = math.Approach(self2.BlowbackCurrent, 0, self2.BlowbackCurrent * FrameTime() * 30)
|
|
end
|
|
|
|
local keys = getKeys(vmbm)
|
|
appendTable(keys, getKeys(self2.GetStatL(self, "BlowbackBoneMods") or self2.BlowbackBoneMods))
|
|
appendTable(keys, getKeys(self2.ViewModelBoneMods_Children))
|
|
|
|
for _,k in pairs(keys) do
|
|
if k == "wepEnt" then goto CONTINUE end
|
|
|
|
local v = vmbm[k] or self2.GetStatL(self, "ViewModelBoneMods." .. k)
|
|
if not v then goto CONTINUE end
|
|
|
|
local vscale, vangle, vpos = v.scale, v.angle, v.pos
|
|
|
|
local bone = vm:LookupBone(k)
|
|
if not bone then goto CONTINUE end
|
|
|
|
local b = self2.GetStatL(self, "BlowbackBoneMods." .. k)
|
|
|
|
if b then
|
|
vscale = Lerp(self2.BlowbackCurrent, vscale, vscale * b.scale)
|
|
vangle = vangle + b.angle * self2.BlowbackCurrent
|
|
vpos = vpos + b.pos * self2.BlowbackCurrent
|
|
end
|
|
|
|
if vm:GetManipulateBoneScale(bone) ~= vscale then
|
|
vm:ManipulateBoneScale(bone, vscale)
|
|
end
|
|
|
|
if vm:GetManipulateBoneAngles(bone) ~= vangle then
|
|
vm:ManipulateBoneAngles(bone, vangle)
|
|
end
|
|
|
|
if vm:GetManipulateBonePosition(bone) ~= vpos then
|
|
vm:ManipulateBonePosition(bone, vpos)
|
|
end
|
|
|
|
::CONTINUE::
|
|
end
|
|
elseif self2.BlowbackBoneMods then
|
|
for bonename, tbl in pairs(self2.BlowbackBoneMods) do
|
|
local bone = vm:LookupBone(bonename)
|
|
|
|
if bone and bone >= 0 then
|
|
bpos = tbl.pos * self2.BlowbackCurrent
|
|
bang = tbl.angle * self2.BlowbackCurrent
|
|
vm:ManipulateBonePosition(bone, bpos)
|
|
vm:ManipulateBoneAngles(bone, bang)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Function Name: ResetBonePositions
|
|
Syntax: self:ResetBonePositions( viewmodel ).
|
|
Returns: Nothing.
|
|
Notes: Resets the bones for a viewmodel.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
function SWEP:ResetBonePositions(val)
|
|
if SERVER then
|
|
self:CallOnClient("ResetBonePositions", "")
|
|
|
|
return
|
|
end
|
|
|
|
local vm = self:GetOwner():GetViewModel()
|
|
if not IsValid(vm) then return end
|
|
if (not vm:GetBoneCount()) then return end
|
|
|
|
for i = 0, vm:GetBoneCount() do
|
|
vm:ManipulateBoneScale(i, Vector(1, 1, 1))
|
|
vm:ManipulateBoneAngles(i, Angle(0, 0, 0))
|
|
vm:ManipulateBonePosition(i, vector_origin)
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Function Name: UpdateWMBonePositions
|
|
Syntax: self:UpdateWMBonePositions( worldmodel ).
|
|
Returns: Nothing.
|
|
Notes: Updates the bones for a worldmodel.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
function SWEP:UpdateWMBonePositions()
|
|
if not self.WorldModelBoneMods then
|
|
self.WorldModelBoneMods = {}
|
|
end
|
|
|
|
local WM_BoneMods = self:GetStatL("WorldModelBoneMods", self.WorldModelBoneMods)
|
|
|
|
if next(WM_BoneMods) then
|
|
for bone = 0, self:GetBoneCount() - 1 do
|
|
local bonemod = WM_BoneMods[self:GetBoneName(bone)]
|
|
if not bonemod then goto CONTINUE end
|
|
|
|
local childscale
|
|
local cur = self:GetBoneParent(bone)
|
|
|
|
while (cur ~= -1) do
|
|
local par = WM_BoneMods[self:GetBoneName(cur)]
|
|
|
|
if par then
|
|
childscale = (childscale or onevec) * (par.scale or onevec)
|
|
end
|
|
|
|
cur = self:GetBoneParent(cur)
|
|
end
|
|
|
|
local s = (bonemod.scale or onevec)
|
|
if childscale then
|
|
s = s * childscale
|
|
end
|
|
|
|
if self:GetManipulateBoneScale(bone) ~= s then
|
|
self:ManipulateBoneScale(bone, s)
|
|
end
|
|
|
|
local a = bonemod.angle or angle_zero
|
|
|
|
if self:GetManipulateBoneAngles(bone) ~= a then
|
|
self:ManipulateBoneAngles(bone, a)
|
|
end
|
|
|
|
local p = bonemod.pos or vector_origin
|
|
|
|
if self:GetManipulateBonePosition(bone) ~= p then
|
|
self:ManipulateBonePosition(bone, p)
|
|
end
|
|
|
|
::CONTINUE::
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Function Name: ResetWMBonePositions
|
|
Syntax: self:ResetWMBonePositions( worldmodel ).
|
|
Returns: Nothing.
|
|
Notes: Resets the bones for a worldmodel.
|
|
Purpose: SWEP Construction Kit Compatibility / Basic Attachments.
|
|
]]
|
|
--
|
|
function SWEP:ResetWMBonePositions(wm)
|
|
if SERVER then
|
|
self:CallOnClient("ResetWMBonePositions", "")
|
|
|
|
return
|
|
end
|
|
|
|
if not wm then
|
|
wm = self
|
|
end
|
|
|
|
if not IsValid(wm) then return end
|
|
|
|
for i = 0, wm:GetBoneCount() - 1 do
|
|
wm:ManipulateBoneScale(i, Vector(1, 1, 1))
|
|
wm:ManipulateBoneAngles(i, Angle(0, 0, 0))
|
|
wm:ManipulateBonePosition(i, vector_origin)
|
|
end
|
|
end
|