Files
wnsrc/addons/tfa-base/lua/weapons/tfa_gun_base/client/mods.lua
lifestorm 73479cff9e Upload
2024-08-04 22:55:00 +03:00

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