· 6 years ago · Jul 27, 2019, 10:30 PM
1--[[
2 © 2017 Thriving Ventures Limited, do not share, re-distribute or modify
3 without permission of its author (gustaf@thrivingventures.com).
4]]
5AddCSLuaFile()
6SWEP.PrintName = "Keys"
7SWEP.Author = "Gonzalolog"
8SWEP.Category = "Monolith RP"
9SWEP.Purpose = "Help"
10SWEP.Slot = 1
11SWEP.SlotPos = 4
12SWEP.Spawnable = true
13SWEP.Primary.ClipSize = -1
14SWEP.Primary.DefaultClip = -1
15SWEP.Primary.Automatic = true
16SWEP.Primary.Ammo = "none"
17SWEP.Secondary.ClipSize = -1
18SWEP.Secondary.DefaultClip = -1
19SWEP.Secondary.Automatic = true
20SWEP.Secondary.Ammo = "none"
21SWEP.DrawAmmo = false
22SWEP.HoldType = "slam"
23SWEP.ViewModelFOV = 62
24SWEP.ViewModelFlip = false
25SWEP.UseHands = true
26SWEP.ViewModel = "models/weapons/c_grenade.mdl"
27SWEP.WorldModel = "models/weapons/w_grenade.mdl"
28SWEP.ShowViewModel = true
29SWEP.ShowWorldModel = false
30
31SWEP.ViewModelBoneMods = {
32 ["ValveBiped.Grenade_body"] = {
33 scale = Vector(0.009, 0.009, 0.009),
34 pos = Vector(0, 0, 0),
35 angle = Angle(0, 0, 0)
36 }
37}
38
39SWEP.VElements = {
40 ["key"] = {
41 type = "Model",
42 model = "models/sentry/monolithkey.mdl",
43 bone = "ValveBiped.Bip01_R_Hand",
44 rel = "",
45 pos = Vector(3, 2.457, 0.5),
46 angle = Angle(-10.896, 150.895, -180.767),
47 size = Vector(1.5, 1.5, 1.5),
48 color = Color(255, 255, 255, 255),
49 surpresslightning = false,
50 material = "",
51 skin = 0,
52 bodygroup = {}
53 }
54}
55
56SWEP.WElements = {
57 ["key"] = {
58 type = "Model",
59 model = "models/sentry/monolithkey.mdl",
60 bone = "ValveBiped.Bip01_R_Hand",
61 rel = "",
62 pos = Vector(2.5, 1.4, 1),
63 angle = Angle(180.107, -90.278, -23.647),
64 size = Vector(1.5, 1.5, 1.5),
65 color = Color(255, 255, 255, 255),
66 surpresslightning = false,
67 material = "",
68 skin = 0,
69 bodygroup = {}
70 }
71}
72
73local function PlayerHasAccess(ply, ent)
74 local owner = nil
75
76 if (ent:IsVehicle()) then
77 owner = ent:GetNWEntity("VehicleOwner")
78 elseif (ent:IsDoor()) then
79 owner = ent.doorOwner or nil
80 elseif (ent:IsPlayer()) then
81 owner = ent
82 end
83
84 if (IsValid(owner)) then
85 owner.MRPKeyAccess = owner.MRPKeyAccess or {}
86
87 for ply in pairs(owner.MRPKeyAccess) do
88 if (not IsValid(ply)) then
89 owner.MRPKeyAccess[ply] = nil
90 end
91 end
92
93 return owner.MRPKeyAccess[ply] or ply == owner
94 end
95
96 if ent:IsDoor() then
97 if ply:GetJobCategory() == ent:GetNW2String("DoorCategory") then
98 return true
99 end
100 end
101
102 return false
103end
104
105function SWEP:HasAccess(ent)
106 return PlayerHasAccess(self.Owner, ent)
107end
108
109function SWEP:PrimaryAttack()
110 if CLIENT then return end
111 local tr = self.Owner:GetEyeTrace()
112 local distance = tr.Entity:GetPos():Distance(self.Owner:GetPos())
113
114 if (distance < 172) then -- standing next to
115 if (tr.Entity:IsDoor()) then
116 if self:HasAccess(tr.Entity) then
117 tr.Entity:Fire("Lock")
118 tr.Entity:SetNW2Bool("Locked", true)
119 end
120 self:SetNextPrimaryFire(CurTime() + 1)
121 self:DoDoorSound(tr.Entity:IsLocked() and 1 or 0)
122 end
123
124 if (tr.Entity:IsVehicle() and self:HasAccess(tr.Entity)) then
125 if(tr.Entity.VC_Lock) then
126 tr.Entity:VC_Lock()
127 else
128 tr.Entity:Fire("lock",0)
129 end
130 tr.Entity:SetNW2Bool("VehicleLocked", true)
131 self:SetNextPrimaryFire(CurTime() + 1)
132 self:DoDoorSound(1)
133 end
134
135 elseif (distance < 1750 and distance >= 172 and self.Owner:IsPremium()) then -- using the remote (premium only)
136 if (tr.Entity:IsVehicle() and self:HasAccess(tr.Entity)) then
137 if(tr.Entity.VC_Lock) then
138 tr.Entity:VC_Lock()
139 else
140 tr.Entity:Fire("lock",0)
141 end
142 tr.Entity:SetNW2Bool("VehicleLocked", true)
143 self:SetNextPrimaryFire(CurTime() + 1.1)
144 self:DoDoorSound(1)
145
146 tr.Entity:VC_SetHazardLights(true)
147 tr.Entity:VC_SetRunningLights(true)
148 tr.Entity:VC_SetHighBeams(true)
149 timer.Simple(1.1, function()
150 tr.Entity:VC_SetHazardLights(false)
151 tr.Entity:VC_SetRunningLights(false)
152 tr.Entity:VC_SetHighBeams(false)
153 end)
154 end
155 end
156end
157
158function SWEP:SecondaryAttack()
159 if CLIENT then return end
160 local tr = self.Owner:GetEyeTrace()
161 local distance = tr.Entity:GetPos():Distance(self.Owner:GetPos())
162
163 if (distance < 172) then -- standing next to
164 if (tr.Entity:IsDoor()) then
165 if self:HasAccess(tr.Entity) then
166 tr.Entity:Fire("Unlock")
167 tr.Entity:SetNW2Bool("Locked", false)
168 end
169 self:SetNextSecondaryFire(CurTime() + 1)
170 self:DoDoorSound(tr.Entity:IsLocked() and 1 or 0)
171 end
172
173 if (tr.Entity:IsVehicle() and self:HasAccess(tr.Entity)) then
174 if(tr.Entity.VC_UnLock) then
175 tr.Entity:VC_UnLock()
176 else
177 tr.Entity:Fire("unlock",0)
178 end
179 tr.Entity:SetNW2Bool("VehicleLocked", false)
180 self:SetNextSecondaryFire(CurTime() + 1)
181 self:DoDoorSound(0)
182 end
183
184 elseif (distance < 1750 and distance >= 172 and self.Owner:IsPremium()) then -- using the remote (premium only)
185 if (tr.Entity:IsVehicle() and self:HasAccess(tr.Entity)) then
186 if(tr.Entity.VC_UnLock) then
187 tr.Entity:VC_UnLock()
188 else
189 tr.Entity:Fire("unlock",0)
190 end
191 tr.Entity:SetNW2Bool("VehicleLocked", false)
192 self:SetNextSecondaryFire(CurTime() + 1.1)
193 self:DoDoorSound(0)
194
195 tr.Entity:VC_SetHazardLights(true)
196 tr.Entity:VC_SetRunningLights(true)
197 tr.Entity:VC_SetHighBeams(true)
198 timer.Simple(1.1, function()
199 tr.Entity:VC_SetHazardLights(false)
200 tr.Entity:VC_SetRunningLights(false)
201 tr.Entity:VC_SetHighBeams(false)
202 end)
203 end
204 end
205end
206
207function SWEP:Reload()
208 if SERVER then
209 local tr = self.Owner:GetEyeTrace()
210 if ((self.NextAssign or 0) < CurTime()) then
211 self.NextAssign = CurTime() + 2
212 if (tr.Entity:IsPlayer() and tr.Entity:GetPos():Distance(self.Owner:GetPos()) < 172) then
213 if (not PlayerHasAccess(tr.Entity, self.Owner)) then
214 tr.Entity:AddNotification("You were granted access to " .. self.Owner:FirstName() .. "'s doors.", 5, 1)
215 self.Owner:AddNotification("You gave door access to " .. tr.Entity:FirstName() .. ".", 5, 1)
216 self.Owner.MRPKeyAccess = self.Owner.MRPKeyAccess or {}
217 self.Owner.MRPKeyAccess[tr.Entity] = true
218 tr.Entity:EmitSound("doors/latchunlocked1.wav")
219 else
220 tr.Entity:AddNotification("Your access to " .. self.Owner:FirstName() .. "'s doors is revoked.", 5, 1)
221 self.Owner:AddNotification("You removed door access from " .. tr.Entity:FirstName().. ".", 5, 1)
222 self.Owner.MRPKeyAccess = self.Owner.MRPKeyAccess or {}
223 self.Owner.MRPKeyAccess[tr.Entity] = nil
224 tr.Entity:EmitSound("doors/latchlocked2.wav")
225 end
226 end
227 end
228 end
229
230 self:SetNextSecondaryFire(CurTime() + 1)
231
232 return true
233end
234
235SWEP.Viewing = 0
236SWEP.SmoothPos = Vector(0, 0, 0)
237SWEP.DestPos = Vector(0, 0, 0)
238
239function SWEP:DoDoorSound(b)
240 local t = self.Owner:GetEyeTrace()
241
242 if (not t.Entity:IsVehicle()) then
243 t.Entity:EmitSound("doors/latch" .. (tonumber(b) == 1 and "locked2" or "unlocked1") .. ".wav")
244 else
245 t.Entity:EmitSound("mrp/car" .. (tonumber(b) == 1 and "lock" or "unlock") .. ".mp3")
246 end
247end
248
249local locked = Material("mrp/hud/lock.png")
250local unlocked = Material("mrp/hud/unlock.png")
251
252function SWEP:DrawHUD()
253 local OwnedProperties = LocalPlayer().OwnedProperties or {}
254 local propertyLookup = {}
255 local numberOwned = table.Count(OwnedProperties)
256 if numberOwned > 0 then
257 local current_y = ScrH()-(20 + numberOwned*30)
258 draw.SimpleText("You own:", "MRPHUD_Voice", ScrW()-245,current_y, Color(235, 235, 235, 255), TEXT_ALIGN_RIGHT)
259
260 for _,build_id in pairs(OwnedProperties) do
261 current_y = current_y + 26
262 draw.SimpleText(Monolith.Properties:GetBuildingByID(build_id).Name or "Unknown Property", "MRPHUD_Voice", ScrW()-245, current_y, Color(150, 235, 100, 255), TEXT_ALIGN_RIGHT)
263
264 propertyLookup[build_id] = true
265 end
266 end
267
268 local t = self.Owner:GetEyeTrace()
269 local distance
270
271 local ent = t.Entity
272
273 local animDelta = Monolith.CalculateDecayInterpolation(0.95)
274
275 local show = false
276 local maxDist = 300
277
278 if (IsValid(ent) and (ent:IsDoor() or ent:IsVehicle())) then
279 distance = ent:GetPos():Distance(self.Owner:GetPos())
280
281 if distance < maxDist then
282 self.DestPos = ent:LocalToWorld(ent:OBBCenter())
283
284 self.CurrentEntity = ent
285 end
286 end
287
288 local currEnt = self.CurrentEntity
289 if IsValid(currEnt) then
290 local scrPos = self.SmoothPos:ToScreen()
291 if not scrPos.visible or self.SmoothPos:Distance(self.DestPos) > maxDist then
292 self.SmoothPos = self.DestPos
293 else
294 self.SmoothPos = LerpVector(animDelta * 4, self.SmoothPos, self.DestPos)
295 end
296
297 if (currEnt:IsDoor()) then
298 show = true
299 self.EntType = "door"
300 elseif (currEnt:IsVehicle()) then
301 if (not self.Owner:InVehicle()) then
302 show = true
303 self.EntType = "vehicle"
304 end
305 end
306
307 if show then
308 if (not distance or distance > maxDist) then
309 show = false
310 end
311 end
312
313 if show then
314 self.Viewing = Lerp(animDelta, self.Viewing, 100)
315 else
316 self.Viewing = Lerp(animDelta, self.Viewing, 0)
317 end
318
319 if self.Viewing > 0 then
320 local destPos = self.DestPos:ToScreen()
321 local pos = self.SmoothPos:ToScreen()
322
323 local viewFract = self.Viewing / 100
324
325 if self.EntType == "vehicle" then
326 surface.SetDrawColor(235, 235, 235, 255 * viewFract)
327 surface.DrawRect(destPos.x, destPos.y - 4, 2, 8)
328 surface.DrawLine(destPos.x, destPos.y, pos.x - 48 * viewFract, pos.y - 48 * viewFract)
329
330 local lockText = (currEnt:GetNW2Bool("VehicleLocked") and "" or "Un-") .. "Locked"
331
332 surface.SetFont("MRPHUD_Voice")
333 local tLockW, tLockH = surface.GetTextSize(lockText)
334 local lineWidth = tLockW
335
336 surface.DrawLine(pos.x - 48 * viewFract, pos.y - 48 * viewFract, pos.x - (52 + lineWidth) * viewFract, pos.y - 48 * viewFract)
337
338 draw.SimpleText((currEnt:GetNW2Bool("VehicleLocked", false) and "" or "Un-") .. "Locked", "MRPHUD_Voice", pos.x - 52 * viewFract, pos.y - 50 * viewFract, Color(235, 235, 235, 255 * viewFract), TEXT_ALIGN_RIGHT)
339 elseif self.EntType == "door" then
340 surface.SetDrawColor(235, 235, 235, 255 * viewFract)
341 surface.DrawRect(destPos.x, destPos.y - 4, 2, 8)
342 surface.DrawLine(destPos.x, destPos.y, pos.x - 48 * viewFract, pos.y - 48 * viewFract)
343
344 local lockText = (currEnt:IsLocked() and "" or "Un-") .. "Locked"
345 local propertyText = currEnt:GetNW2String("property.name", "")
346
347 surface.SetFont("MRPHUD_Voice")
348 local tLockW, tLockH = surface.GetTextSize(lockText)
349 local tPropW, tPropH = surface.GetTextSize(propertyText)
350
351 local lineWidth = math.max(tPropW, tLockW)
352
353 local propertyColor = Color(235, 100, 50)
354 if propertyLookup[currEnt:GetNW2Int("property.uid")] or currEnt:GetNW2String("DoorCategory") == LocalPlayer():GetJobCategory() then
355 propertyColor = Color(150, 235, 100)
356 end
357
358 surface.DrawLine(pos.x - 48 * viewFract, pos.y - 48 * viewFract, pos.x - (52 + lineWidth) * viewFract, pos.y - 48 * viewFract)
359 draw.SimpleText(lockText, "MRPHUD_Voice", pos.x - 52 * viewFract, pos.y - 50 * viewFract, Color(235, 235, 235, 255 * viewFract), TEXT_ALIGN_RIGHT)
360 draw.SimpleText(propertyText, "MRPHUD_Voice", pos.x - 52 * viewFract, pos.y - 75 * viewFract, ColorAlpha(propertyColor, 255 * viewFract), TEXT_ALIGN_RIGHT)
361
362 --surface.SetMaterial(currEnt:IsLocked() && unlocked || locked )
363 --surface.DrawTexturedRect(pos.x-48,pos.y-48,128,128)
364 end
365 end
366 end
367end
368
369function SWEP:Initialize()
370 self:SetWeaponHoldType( self.HoldType )
371
372 if CLIENT then
373 -- Create a new table for every weapon instance
374 self.VElements = table.FullCopy(self.VElements)
375 self.WElements = table.FullCopy(self.WElements)
376 self.ViewModelBoneMods = table.FullCopy(self.ViewModelBoneMods)
377 self:CreateModels(self.VElements) -- create viewmodels
378 self:CreateModels(self.WElements) -- create worldmodels
379
380 -- init view model bone build function
381 if IsValid(self.Owner) then
382 local vm = self.Owner:GetViewModel()
383
384 if IsValid(vm) then
385 self:ResetBonePositions(vm)
386
387 -- Init viewmodel visibility
388 if (self.ShowViewModel == nil or self.ShowViewModel) then
389 vm:SetColor(Color(255, 255, 255, 255))
390 -- we set the alpha to 1 instead of 0 because else ViewModelDrawn stops being called
391 -- ^ stopped working in GMod 13 because you have to do Entity:SetRenderMode(1) for translucency to kick in
392 -- 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
393 else
394 vm:SetColor(Color(255, 255, 255, 1))
395 vm:SetMaterial("Debug/hsv")
396 end
397 end
398 end
399 end
400end
401
402function SWEP:Holster()
403 if CLIENT and IsValid(self.Owner) then
404 local vm = self.Owner:GetViewModel()
405
406 if IsValid(vm) then
407 self:ResetBonePositions(vm)
408 end
409 end
410
411 return true
412end
413
414function SWEP:OnRemove()
415 self:Holster()
416end
417
418if CLIENT then
419 SWEP.vRenderOrder = nil
420
421 function SWEP:ViewModelDrawn()
422 local vm = self.Owner:GetViewModel()
423 if not IsValid(vm) then return end
424 if (not self.VElements) then return end
425 self:UpdateBonePositions(vm)
426
427 if (not self.vRenderOrder) then
428 -- we build a render order because sprites need to be drawn after models
429 self.vRenderOrder = {}
430
431 for k, v in pairs(self.VElements) do
432 if (v.type == "Model") then
433 table.insert(self.vRenderOrder, 1, k)
434 elseif (v.type == "Sprite" or v.type == "Quad") then
435 table.insert(self.vRenderOrder, k)
436 end
437 end
438 end
439
440 for k, name in ipairs(self.vRenderOrder) do
441 local v = self.VElements[name]
442
443 if (not v) then
444 self.vRenderOrder = nil
445 break
446 end
447
448 if (v.hide) then continue end
449 local model = v.modelEnt
450 local sprite = v.spriteMaterial
451 if (not v.bone) then continue end
452 local pos, ang = self:GetBoneOrientation(self.VElements, v, vm)
453 if (not pos) then continue end
454
455 if (v.type == "Model" and IsValid(model)) then
456 model:SetPos(pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z)
457 ang:RotateAroundAxis(ang:Up(), v.angle.y)
458 ang:RotateAroundAxis(ang:Right(), v.angle.p)
459 ang:RotateAroundAxis(ang:Forward(), v.angle.r)
460 model:SetAngles(ang)
461 --model:SetModelScale(v.size)
462 local matrix = Matrix()
463 matrix:Scale(v.size)
464 model:EnableMatrix("RenderMultiply", matrix)
465
466 if (v.material == "") then
467 model:SetMaterial("")
468 elseif (model:GetMaterial() ~= v.material) then
469 model:SetMaterial(v.material)
470 end
471
472 if (v.skin and v.skin ~= model:GetSkin()) then
473 model:SetSkin(v.skin)
474 end
475
476 if (v.bodygroup) then
477 for k, v in pairs(v.bodygroup) do
478 if (model:GetBodygroup(k) ~= v) then
479 model:SetBodygroup(k, v)
480 end
481 end
482 end
483
484 if (v.surpresslightning) then
485 render.SuppressEngineLighting(true)
486 end
487
488 render.SetColorModulation(v.color.r / 255, v.color.g / 255, v.color.b / 255)
489 render.SetBlend(v.color.a / 255)
490 model:DrawModel()
491 render.SetBlend(1)
492 render.SetColorModulation(1, 1, 1)
493
494 if (v.surpresslightning) then
495 render.SuppressEngineLighting(false)
496 end
497 elseif (v.type == "Sprite" and sprite) then
498 local drawpos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
499 render.SetMaterial(sprite)
500 render.DrawSprite(drawpos, v.size.x, v.size.y, v.color)
501 elseif (v.type == "Quad" and v.draw_func) then
502 local drawpos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
503 ang:RotateAroundAxis(ang:Up(), v.angle.y)
504 ang:RotateAroundAxis(ang:Right(), v.angle.p)
505 ang:RotateAroundAxis(ang:Forward(), v.angle.r)
506 cam.Start3D2D(drawpos, ang, v.size)
507 v.draw_func(self)
508 cam.End3D2D()
509 end
510 end
511 end
512
513 SWEP.wRenderOrder = nil
514
515 function SWEP:DrawWorldModel()
516 if (self.ShowWorldModel == nil or self.ShowWorldModel) then
517 self:DrawModel()
518 end
519
520 if (not self.WElements) then return end
521
522 if (not self.wRenderOrder) then
523 self.wRenderOrder = {}
524
525 for k, v in pairs(self.WElements) do
526 if (v.type == "Model") then
527 table.insert(self.wRenderOrder, 1, k)
528 elseif (v.type == "Sprite" or v.type == "Quad") then
529 table.insert(self.wRenderOrder, k)
530 end
531 end
532 end
533
534 if (IsValid(self.Owner)) then
535 bone_ent = self.Owner
536 -- when the weapon is dropped
537 else
538 bone_ent = self
539 end
540
541 for k, name in pairs(self.wRenderOrder) do
542 local v = self.WElements[name]
543
544 if (not v) then
545 self.wRenderOrder = nil
546 break
547 end
548
549 if (v.hide) then continue end
550 local pos, ang
551
552 if (v.bone) then
553 pos, ang = self:GetBoneOrientation(self.WElements, v, bone_ent)
554 else
555 pos, ang = self:GetBoneOrientation(self.WElements, v, bone_ent, "ValveBiped.Bip01_R_Hand")
556 end
557
558 if (not pos) then continue end
559 local model = v.modelEnt
560 local sprite = v.spriteMaterial
561
562 if (v.type == "Model" and IsValid(model)) then
563 model:SetPos(pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z)
564 ang:RotateAroundAxis(ang:Up(), v.angle.y)
565 ang:RotateAroundAxis(ang:Right(), v.angle.p)
566 ang:RotateAroundAxis(ang:Forward(), v.angle.r)
567 model:SetAngles(ang)
568 --model:SetModelScale(v.size)
569 local matrix = Matrix()
570 matrix:Scale(v.size)
571 model:EnableMatrix("RenderMultiply", matrix)
572
573 if (v.material == "") then
574 model:SetMaterial("")
575 elseif (model:GetMaterial() ~= v.material) then
576 model:SetMaterial(v.material)
577 end
578
579 if (v.skin and v.skin ~= model:GetSkin()) then
580 model:SetSkin(v.skin)
581 end
582
583 if (v.bodygroup) then
584 for k, v in pairs(v.bodygroup) do
585 if (model:GetBodygroup(k) ~= v) then
586 model:SetBodygroup(k, v)
587 end
588 end
589 end
590
591 if (v.surpresslightning) then
592 render.SuppressEngineLighting(true)
593 end
594
595 render.SetColorModulation(v.color.r / 255, v.color.g / 255, v.color.b / 255)
596 render.SetBlend(v.color.a / 255)
597 model:DrawModel()
598 render.SetBlend(1)
599 render.SetColorModulation(1, 1, 1)
600
601 if (v.surpresslightning) then
602 render.SuppressEngineLighting(false)
603 end
604 elseif (v.type == "Sprite" and sprite) then
605 local drawpos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
606 render.SetMaterial(sprite)
607 render.DrawSprite(drawpos, v.size.x, v.size.y, v.color)
608 elseif (v.type == "Quad" and v.draw_func) then
609 local drawpos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
610 ang:RotateAroundAxis(ang:Up(), v.angle.y)
611 ang:RotateAroundAxis(ang:Right(), v.angle.p)
612 ang:RotateAroundAxis(ang:Forward(), v.angle.r)
613 cam.Start3D2D(drawpos, ang, v.size)
614 v.draw_func(self)
615 cam.End3D2D()
616 end
617 end
618 end
619
620 function SWEP:GetBoneOrientation(basetab, tab, ent, bone_override)
621 local bone, pos, ang
622
623 if (tab.rel and tab.rel ~= "") then
624 local v = basetab[tab.rel]
625 if (not v) then return end
626 -- Technically, if there exists an element with the same name as a bone
627 -- you can get in an infinite loop. Let's just hope nobody's that stupid.
628 pos, ang = self:GetBoneOrientation(basetab, v, ent)
629 if (not pos) then return end
630 pos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z
631 ang:RotateAroundAxis(ang:Up(), v.angle.y)
632 ang:RotateAroundAxis(ang:Right(), v.angle.p)
633 ang:RotateAroundAxis(ang:Forward(), v.angle.r)
634 -- Fixes mirrored models
635 else
636 bone = ent:LookupBone(bone_override or tab.bone)
637 if (not bone) then return end
638 pos, ang = Vector(0, 0, 0), Angle(0, 0, 0)
639 local m = ent:GetBoneMatrix(bone)
640
641 if (m) then
642 pos, ang = m:GetTranslation(), m:GetAngles()
643 end
644
645 if (IsValid(self.Owner) and self.Owner:IsPlayer() and ent == self.Owner:GetViewModel() and self.ViewModelFlip) then
646 ang.r = -ang.r
647 end
648 end
649
650 return pos, ang
651 end
652
653 function SWEP:CreateModels(tab)
654 if (not tab) then return end
655
656 -- Create the clientside models here because Garry says we can't do it in the render hook
657 for k, v in pairs(tab) do
658 if (v.type == "Model" and v.model and v.model ~= "" and (not IsValid(v.modelEnt) or v.createdModel ~= v.model) and string.find(v.model, ".mdl") and file.Exists(v.model, "GAME")) then
659 v.modelEnt = ClientsideModel(v.model, RENDER_GROUP_VIEW_MODEL_OPAQUE)
660
661 if (IsValid(v.modelEnt)) then
662 v.modelEnt:SetPos(self:GetPos())
663 v.modelEnt:SetAngles(self:GetAngles())
664 v.modelEnt:SetParent(self)
665 v.modelEnt:SetNoDraw(true)
666 v.createdModel = v.model
667 else
668 v.modelEnt = nil
669 end
670 -- make sure we create a unique name based on the selected options
671 elseif (v.type == "Sprite" and v.sprite and v.sprite ~= "" and (not v.spriteMaterial or v.createdSprite ~= v.sprite) and file.Exists("materials/" .. v.sprite .. ".vmt", "GAME")) then
672 local name = v.sprite .. "-"
673
674 local params = {
675 ["$basetexture"] = v.sprite
676 }
677
678 local tocheck = {"nocull", "additive", "vertexalpha", "vertexcolor", "ignorez"}
679
680 for i, j in pairs(tocheck) do
681 if (v[j]) then
682 params["$" .. j] = 1
683 name = name .. "1"
684 else
685 name = name .. "0"
686 end
687 end
688
689 v.createdSprite = v.sprite
690 v.spriteMaterial = CreateMaterial(name, "UnlitGeneric", params)
691 end
692 end
693 end
694
695 local allbones
696 local hasGarryFixedBoneScalingYet = false
697
698 function SWEP:UpdateBonePositions(vm)
699 if self.ViewModelBoneMods then
700 if (not vm:GetBoneCount()) then return end
701 -- !! WORKAROUND !! //
702 -- We need to check all model names :/
703 local loopthrough = self.ViewModelBoneMods
704
705 if (not hasGarryFixedBoneScalingYet) then
706 allbones = {}
707
708 for i = 0, vm:GetBoneCount() do
709 local bonename = vm:GetBoneName(i)
710
711 if (self.ViewModelBoneMods[bonename]) then
712 allbones[bonename] = self.ViewModelBoneMods[bonename]
713 else
714 allbones[bonename] = {
715 scale = Vector(1, 1, 1),
716 pos = Vector(0, 0, 0),
717 angle = Angle(0, 0, 0)
718 }
719 end
720 end
721
722 loopthrough = allbones
723 end
724
725 -- !! ----------- !! //
726 for k, v in pairs(loopthrough) do
727 local bone = vm:LookupBone(k)
728 if (not bone) then continue end
729 -- !! WORKAROUND !! //
730 local s = Vector(v.scale.x, v.scale.y, v.scale.z)
731 local p = Vector(v.pos.x, v.pos.y, v.pos.z)
732 local ms = Vector(1, 1, 1)
733
734 if (not hasGarryFixedBoneScalingYet) then
735 local cur = vm:GetBoneParent(bone)
736
737 while (cur >= 0) do
738 local pscale = loopthrough[vm:GetBoneName(cur)].scale
739 ms = ms * pscale
740 cur = vm:GetBoneParent(cur)
741 end
742 end
743
744 s = s * ms
745
746 -- !! ----------- !! //
747 if vm:GetManipulateBoneScale(bone) ~= s then
748 vm:ManipulateBoneScale(bone, s)
749 end
750
751 if vm:GetManipulateBoneAngles(bone) ~= v.angle then
752 vm:ManipulateBoneAngles(bone, v.angle)
753 end
754
755 if vm:GetManipulateBonePosition(bone) ~= p then
756 vm:ManipulateBonePosition(bone, p)
757 end
758 end
759 else
760 self:ResetBonePositions(vm)
761 end
762 end
763
764 function SWEP:ResetBonePositions(vm)
765 if (not vm:GetBoneCount()) then return end
766
767 for i = 0, vm:GetBoneCount() do
768 vm:ManipulateBoneScale(i, Vector(1, 1, 1))
769 vm:ManipulateBoneAngles(i, Angle(0, 0, 0))
770 vm:ManipulateBonePosition(i, Vector(0, 0, 0))
771 end
772 end
773
774 --[[*************************
775 Global utility code
776 *************************]]
777 -- Fully copies the table, meaning all tables inside this table are copied too and so on (normal table.Copy copies only their reference).
778 -- Does not copy entities of course, only copies their reference.
779 -- WARNING: do not use on tables that contain themselves somewhere down the line or you'll get an infinite loop
780 function table.FullCopy(tab)
781 if (not tab) then return nil end
782 local res = {}
783
784 for k, v in pairs(tab) do
785 if (type(v) == "table") then
786 res[k] = table.FullCopy(v) -- recursion ho!
787 elseif (type(v) == "Vector") then
788 res[k] = Vector(v.x, v.y, v.z)
789 elseif (type(v) == "Angle") then
790 res[k] = Angle(v.p, v.y, v.r)
791 else
792 res[k] = v
793 end
794 end
795
796 return res
797 end
798end