· 5 years ago · Apr 01, 2020, 06:52 PM
1/********************************************************
2 SWEP Construction Kit base code
3 Created by Clavus
4 Available for public use, thread at:
5 facepunch.com/threads/1032378
6
7
8 DESCRIPTION:
9 This script is meant for experienced scripters
10 that KNOW WHAT THEY ARE DOING. Don't come to me
11 with basic Lua questions.
12
13 Just copy into your SWEP or SWEP base of choice
14 and merge with your own code.
15
16 The SWEP.VElements, SWEP.WElements and
17 SWEP.ViewModelBoneMods tables are all optional
18 and only have to be visible to the client.
19********************************************************/
20
21function SWEP:Initialize()
22
23 // other initialize code goes here
24
25 if CLIENT then
26
27 // Create a new table for every weapon instance
28 self.VElements = table.FullCopy( self.VElements )
29 self.WElements = table.FullCopy( self.WElements )
30 self.ViewModelBoneMods = table.FullCopy( self.ViewModelBoneMods )
31
32 self:CreateModels(self.VElements) // create viewmodels
33 self:CreateModels(self.WElements) // create worldmodels
34
35 // init view model bone build function
36 if IsValid(self.Owner) then
37 local vm = self.Owner:GetViewModel()
38 if IsValid(vm) then
39 self:ResetBonePositions(vm)
40
41 // Init viewmodel visibility
42 if (self.ShowViewModel == nil or self.ShowViewModel) then
43 vm:SetColor(Color(255,255,255,255))
44 else
45 // we set the alpha to 1 instead of 0 because else ViewModelDrawn stops being called
46 vm:SetColor(Color(255,255,255,1))
47 // ^ stopped working in GMod 13 because you have to do Entity:SetRenderMode(1) for translucency to kick in
48 // however for some reason the view model resets to render mode 0 every frame so we just apply a debug material to prevent it from drawing
49 vm:SetMaterial("Debug/hsv")
50 end
51 end
52 end
53
54 end
55
56end
57
58function SWEP:Holster()
59
60 if CLIENT and IsValid(self.Owner) then
61 local vm = self.Owner:GetViewModel()
62 if IsValid(vm) then
63 self:ResetBonePositions(vm)
64 end
65 end
66
67 return true
68end
69
70function SWEP:OnRemove()
71 self:Holster()
72end
73
74if CLIENT then
75
76 SWEP.vRenderOrder = nil
77 function SWEP:ViewModelDrawn()
78
79 local vm = self.Owner:GetViewModel()
80 if !IsValid(vm) then return end
81
82 if (!self.VElements) then return end
83
84 self:UpdateBonePositions(vm)
85
86 if (!self.vRenderOrder) then
87
88 // we build a render order because sprites need to be drawn after models
89 self.vRenderOrder = {}
90
91 for k, v in pairs( self.VElements ) do
92 if (v.type == "Model") then
93 table.insert(self.vRenderOrder, 1, k)
94 elseif (v.type == "Sprite" or v.type == "Quad") then
95 table.insert(self.vRenderOrder, k)
96 end
97 end
98
99 end
100
101 for k, name in ipairs( self.vRenderOrder ) do
102
103 local v = self.VElements[name]
104 if (!v) then self.vRenderOrder = nil break end
105 if (v.hide) then continue end
106
107 local model = v.modelEnt
108 local sprite = v.spriteMaterial
109
110 if (!v.bone) then continue end
111
112 local pos, ang = self:GetBoneOrientation( self.VElements, v, vm )
113
114 if (!pos) then continue end
115
116 if (v.type == "Model" and IsValid(model)) then
117
118 model:SetPos(pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z )
119 ang:RotateAroundAxis(ang:Up(), v.angle.y)
120 ang:RotateAroundAxis(ang:Right(), v.angle.p)
121 ang:RotateAroundAxis(ang:Forward(), v.angle.r)
122
123 model:SetAngles(ang)
124 //model:SetModelScale(v.size)
125 local matrix = Matrix()
126 matrix:Scale(v.size)
127 model:EnableMatrix( "RenderMultiply", matrix )
128
129 if (v.material == "") then
130 model:SetMaterial("")
131 elseif (model:GetMaterial() != v.material) then
132 model:SetMaterial( v.material )
133 end
134
135 if (v.skin and v.skin != model:GetSkin()) then
136 model:SetSkin(v.skin)
137 end
138
139 if (v.bodygroup) then
140 for k, v in pairs( v.bodygroup ) do
141 if (model:GetBodygroup(k) != v) then
142 model:SetBodygroup(k, v)
143 end
144 end
145 end
146
147 if (v.surpresslightning) then
148 render.SuppressEngineLighting(true)
149 end
150
151 render.SetColorModulation(v.color.r/255, v.color.g/255, v.color.b/255)
152 render.SetBlend(v.color.a/255)
153 model:DrawModel()
154 render.SetBlend(1)
155 render.SetColorModulation(1, 1, 1)
156
157 if (v.surpresslightning) then
158 render.SuppressEngineLighting(false)
159 end
160
161 elseif (v.type == "Sprite" and sprite) then
162
163 local drawpos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
164 render.SetMaterial(sprite)
165 render.DrawSprite(drawpos, v.size.x, v.size.y, v.color)
166
167 elseif (v.type == "Quad" and v.draw_func) then
168
169 local drawpos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
170 ang:RotateAroundAxis(ang:Up(), v.angle.y)
171 ang:RotateAroundAxis(ang:Right(), v.angle.p)
172 ang:RotateAroundAxis(ang:Forward(), v.angle.r)
173
174 cam.Start3D2D(drawpos, ang, v.size)
175 v.draw_func( self )
176 cam.End3D2D()
177
178 end
179
180 end
181
182 end
183
184 SWEP.wRenderOrder = nil
185 function SWEP:DrawWorldModel()
186
187 if (self.ShowWorldModel == nil or self.ShowWorldModel) then
188 self:DrawModel()
189 end
190
191 if (!self.WElements) then return end
192
193 if (!self.wRenderOrder) then
194
195 self.wRenderOrder = {}
196
197 for k, v in pairs( self.WElements ) do
198 if (v.type == "Model") then
199 table.insert(self.wRenderOrder, 1, k)
200 elseif (v.type == "Sprite" or v.type == "Quad") then
201 table.insert(self.wRenderOrder, k)
202 end
203 end
204
205 end
206
207 if (IsValid(self.Owner)) then
208 bone_ent = self.Owner
209 else
210 // when the weapon is dropped
211 bone_ent = self
212 end
213
214 for k, name in pairs( self.wRenderOrder ) do
215
216 local v = self.WElements[name]
217 if (!v) then self.wRenderOrder = nil break end
218 if (v.hide) then continue end
219
220 local pos, ang
221
222 if (v.bone) then
223 pos, ang = self:GetBoneOrientation( self.WElements, v, bone_ent )
224 else
225 pos, ang = self:GetBoneOrientation( self.WElements, v, bone_ent, "ValveBiped.Bip01_R_Hand" )
226 end
227
228 if (!pos) then continue end
229
230 local model = v.modelEnt
231 local sprite = v.spriteMaterial
232
233 if (v.type == "Model" and IsValid(model)) then
234
235 model:SetPos(pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z )
236 ang:RotateAroundAxis(ang:Up(), v.angle.y)
237 ang:RotateAroundAxis(ang:Right(), v.angle.p)
238 ang:RotateAroundAxis(ang:Forward(), v.angle.r)
239
240 model:SetAngles(ang)
241 //model:SetModelScale(v.size)
242 local matrix = Matrix()
243 matrix:Scale(v.size)
244 model:EnableMatrix( "RenderMultiply", matrix )
245
246 if (v.material == "") then
247 model:SetMaterial("")
248 elseif (model:GetMaterial() != v.material) then
249 model:SetMaterial( v.material )
250 end
251
252 if (v.skin and v.skin != model:GetSkin()) then
253 model:SetSkin(v.skin)
254 end
255
256 if (v.bodygroup) then
257 for k, v in pairs( v.bodygroup ) do
258 if (model:GetBodygroup(k) != v) then
259 model:SetBodygroup(k, v)
260 end
261 end
262 end
263
264 if (v.surpresslightning) then
265 render.SuppressEngineLighting(true)
266 end
267
268 render.SetColorModulation(v.color.r/255, v.color.g/255, v.color.b/255)
269 render.SetBlend(v.color.a/255)
270 model:DrawModel()
271 render.SetBlend(1)
272 render.SetColorModulation(1, 1, 1)
273
274 if (v.surpresslightning) then
275 render.SuppressEngineLighting(false)
276 end
277
278 elseif (v.type == "Sprite" and sprite) then
279
280 local drawpos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
281 render.SetMaterial(sprite)
282 render.DrawSprite(drawpos, v.size.x, v.size.y, v.color)
283
284 elseif (v.type == "Quad" and v.draw_func) then
285
286 local drawpos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
287 ang:RotateAroundAxis(ang:Up(), v.angle.y)
288 ang:RotateAroundAxis(ang:Right(), v.angle.p)
289 ang:RotateAroundAxis(ang:Forward(), v.angle.r)
290
291 cam.Start3D2D(drawpos, ang, v.size)
292 v.draw_func( self )
293 cam.End3D2D()
294
295 end
296
297 end
298
299 end
300
301 function SWEP:GetBoneOrientation( basetab, tab, ent, bone_override )
302
303 local bone, pos, ang
304 if (tab.rel and tab.rel != "") then
305
306 local v = basetab[tab.rel]
307
308 if (!v) then return end
309
310 // Technically, if there exists an element with the same name as a bone
311 // you can get in an infinite loop. Let's just hope nobody's that stupid.
312 pos, ang = self:GetBoneOrientation( basetab, v, ent )
313
314 if (!pos) then return end
315
316 pos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
317 ang:RotateAroundAxis(ang:Up(), v.angle.y)
318 ang:RotateAroundAxis(ang:Right(), v.angle.p)
319 ang:RotateAroundAxis(ang:Forward(), v.angle.r)
320
321 else
322
323 bone = ent:LookupBone(bone_override or tab.bone)
324
325 if (!bone) then return end
326
327 pos, ang = Vector(0,0,0), Angle(0,0,0)
328 local m = ent:GetBoneMatrix(bone)
329 if (m) then
330 pos, ang = m:GetTranslation(), m:GetAngles()
331 end
332
333 if (IsValid(self.Owner) and self.Owner:IsPlayer() and
334 ent == self.Owner:GetViewModel() and self.ViewModelFlip) then
335 ang.r = -ang.r // Fixes mirrored models
336 end
337
338 end
339
340 return pos, ang
341 end
342
343 function SWEP:CreateModels( tab )
344
345 if (!tab) then return end
346
347 // Create the clientside models here because Garry says we can't do it in the render hook
348 for k, v in pairs( tab ) do
349 if (v.type == "Model" and v.model and v.model != "" and (!IsValid(v.modelEnt) or v.createdModel != v.model) and
350 string.find(v.model, ".mdl") and file.Exists (v.model, "GAME") ) then
351
352 v.modelEnt = ClientsideModel(v.model, RENDER_GROUP_VIEW_MODEL_OPAQUE)
353 if (IsValid(v.modelEnt)) then
354 v.modelEnt:SetPos(self:GetPos())
355 v.modelEnt:SetAngles(self:GetAngles())
356 v.modelEnt:SetParent(self)
357 v.modelEnt:SetNoDraw(true)
358 v.createdModel = v.model
359 else
360 v.modelEnt = nil
361 end
362
363 elseif (v.type == "Sprite" and v.sprite and v.sprite != "" and (!v.spriteMaterial or v.createdSprite != v.sprite)
364 and file.Exists ("materials/"..v.sprite..".vmt", "GAME")) then
365
366 local name = v.sprite.."-"
367 local params = { ["$basetexture"] = v.sprite }
368 // make sure we create a unique name based on the selected options
369 local tocheck = { "nocull", "additive", "vertexalpha", "vertexcolor", "ignorez" }
370 for i, j in pairs( tocheck ) do
371 if (v[j]) then
372 params["$"..j] = 1
373 name = name.."1"
374 else
375 name = name.."0"
376 end
377 end
378
379 v.createdSprite = v.sprite
380 v.spriteMaterial = CreateMaterial(name,"UnlitGeneric",params)
381
382 end
383 end
384
385 end
386
387 local allbones
388 local hasGarryFixedBoneScalingYet = false
389
390 function SWEP:UpdateBonePositions(vm)
391
392 if self.ViewModelBoneMods then
393
394 if (!vm:GetBoneCount()) then return end
395
396 // !! WORKAROUND !! //
397 // We need to check all model names :/
398 local loopthrough = self.ViewModelBoneMods
399 if (!hasGarryFixedBoneScalingYet) then
400 allbones = {}
401 for i=0, vm:GetBoneCount() do
402 local bonename = vm:GetBoneName(i)
403 if (self.ViewModelBoneMods[bonename]) then
404 allbones[bonename] = self.ViewModelBoneMods[bonename]
405 else
406 allbones[bonename] = {
407 scale = Vector(1,1,1),
408 pos = Vector(0,0,0),
409 angle = Angle(0,0,0)
410 }
411 end
412 end
413
414 loopthrough = allbones
415 end
416 // !! ----------- !! //
417
418 for k, v in pairs( loopthrough ) do
419 local bone = vm:LookupBone(k)
420 if (!bone) then continue end
421
422 // !! WORKAROUND !! //
423 local s = Vector(v.scale.x,v.scale.y,v.scale.z)
424 local p = Vector(v.pos.x,v.pos.y,v.pos.z)
425 local ms = Vector(1,1,1)
426 if (!hasGarryFixedBoneScalingYet) then
427 local cur = vm:GetBoneParent(bone)
428 while(cur >= 0) do
429 local pscale = loopthrough[vm:GetBoneName(cur)].scale
430 ms = ms * pscale
431 cur = vm:GetBoneParent(cur)
432 end
433 end
434
435 s = s * ms
436 // !! ----------- !! //
437
438 if vm:GetManipulateBoneScale(bone) != s then
439 vm:ManipulateBoneScale( bone, s )
440 end
441 if vm:GetManipulateBoneAngles(bone) != v.angle then
442 vm:ManipulateBoneAngles( bone, v.angle )
443 end
444 if vm:GetManipulateBonePosition(bone) != p then
445 vm:ManipulateBonePosition( bone, p )
446 end
447 end
448 else
449 self:ResetBonePositions(vm)
450 end
451
452 end
453
454 function SWEP:ResetBonePositions(vm)
455
456 if (!vm:GetBoneCount()) then return end
457 for i=0, vm:GetBoneCount() do
458 vm:ManipulateBoneScale( i, Vector(1, 1, 1) )
459 vm:ManipulateBoneAngles( i, Angle(0, 0, 0) )
460 vm:ManipulateBonePosition( i, Vector(0, 0, 0) )
461 end
462
463 end
464
465 /**************************
466 Global utility code
467 **************************/
468
469 // Fully copies the table, meaning all tables inside this table are copied too and so on (normal table.Copy copies only their reference).
470 // Does not copy entities of course, only copies their reference.
471 // WARNING: do not use on tables that contain themselves somewhere down the line or you'll get an infinite loop
472 function table.FullCopy( tab )
473
474 if (!tab) then return nil end
475
476 local res = {}
477 for k, v in pairs( tab ) do
478 if (type(v) == "table") then
479 res[k] = table.FullCopy(v) // recursion ho!
480 elseif (type(v) == "Vector") then
481 res[k] = Vector(v.x, v.y, v.z)
482 elseif (type(v) == "Angle") then
483 res[k] = Angle(v.p, v.y, v.r)
484 else
485 res[k] = v
486 end
487 end
488
489 return res
490
491 end
492
493end