· 8 years ago · Mar 12, 2018, 11:06 PM
1-- Includes a file from the prefix.
2function nut.util.include(fileName, state)
3 if (!fileName) then
4 error("[NutScript] No file name specified for including.")
5 end
6
7 -- Only include server-side if we're on the server.
8 if ((state == "server" or fileName:find("sv_")) and SERVER) then
9 include(fileName)
10 -- Shared is included by both server and client.
11 elseif (state == "shared" or fileName:find("sh_")) then
12 if (SERVER) then
13 -- Send the file to the client if shared so they can run it.
14 AddCSLuaFile(fileName)
15 end
16
17 include(fileName)
18 -- File is sent to client, included on client.
19 elseif (state == "client" or fileName:find("cl_")) then
20 if (SERVER) then
21 AddCSLuaFile(fileName)
22 else
23 include(fileName)
24 end
25 end
26end
27
28-- Include files based off the prefix within a directory.
29function nut.util.includeDir(directory, fromLua)
30 -- By default, we include relatively to NutScript.
31 local baseDir = "nutscript"
32
33 -- If we're in a schema, include relative to the schema.
34 if (SCHEMA and SCHEMA.folder and SCHEMA.loading) then
35 baseDir = SCHEMA.folder.."/schema/"
36 else
37 baseDir = baseDir.."/gamemode/"
38 end
39
40 -- Find all of the files within the directory.
41 for k, v in ipairs(file.Find((fromLua and "" or baseDir)..directory.."/*.lua", "LUA")) do
42 -- Include the file from the prefix.
43 nut.util.include(directory.."/"..v)
44 end
45end
46
47-- Returns the address:port of the server.
48function nut.util.getAddress()
49 local address = tonumber(GetConVarString("hostip"))
50
51 if (!address) then
52 return "127.0.0.1"..":"..GetConVarString("hostport")
53 end
54
55 local ip = {}
56 ip[1] = bit.rshift(bit.band(address, 0xFF000000), 24)
57 ip[2] = bit.rshift(bit.band(address, 0x00FF0000), 16)
58 ip[3] = bit.rshift(bit.band(address, 0x0000FF00), 8)
59 ip[4] = bit.band(address, 0x000000FF)
60 return table.concat(ip, ".")..":"..GetConVarString("hostport")
61end
62
63-- Returns a table of admin players
64function nut.util.getAdmins(isSuper)
65 local admins = {}
66
67 for k, v in ipairs(player.GetAll()) do
68 if (isSuper) then
69 if (v:IsSuperAdmin()) then
70 table.insert(admins, v)
71 end
72 else
73 if (v:IsAdmin()) then
74 table.insert(admins, v)
75 end
76 end
77 end
78
79 return admins
80end
81
82-- Returns a single cached copy of a material or creates it if it doesn't exist.
83function nut.util.getMaterial(materialPath)
84 -- Cache the material.
85 nut.util.cachedMaterials = nut.util.cachedMaterials or {}
86 nut.util.cachedMaterials[materialPath] = nut.util.cachedMaterials[materialPath] or Material(materialPath)
87
88 return nut.util.cachedMaterials[materialPath]
89end
90
91-- Finds a player by matching their name or steam id.
92function nut.util.findPlayer(identifier, allowPatterns)
93 if (string.find(identifier, "STEAM_(%d+):(%d+):(%d+)")) then
94 return player.GetBySteamID(identifier)
95 end
96
97 if (!allowPatterns) then
98 identifier = string.PatternSafe(identifier)
99 end
100
101 for k, v in ipairs(player.GetAll()) do
102 if (nut.util.stringMatches(v:Name(), identifier)) then
103 return v
104 end
105 end
106end
107
108-- Returns whether or a not a string matches.
109function nut.util.stringMatches(a, b)
110 if (a and b) then
111 local a2, b2 = a:lower(), b:lower()
112
113 -- Check if the actual letters match.
114 if (a == b) then return true end
115 if (a2 == b2) then return true end
116
117 -- Be less strict and search.
118 if (a:find(b)) then return true end
119 if (a2:find(b2)) then return true end
120 end
121
122 return false
123end
124
125local ADJUST_SOUND = SoundDuration("npc/metropolice/pain1.wav") > 0 and "" or "../../hl2/sound/"
126
127-- Emits sounds one after the other from an entity.
128function nut.util.emitQueuedSounds(entity, sounds, delay, spacing, volume, pitch)
129 -- Let there be a delay before any sound is played.
130 delay = delay or 0
131 spacing = spacing or 0.1
132
133 -- Loop through all of the sounds.
134 for k, v in ipairs(sounds) do
135 local postSet, preSet = 0, 0
136
137 -- Determine if this sound has special time offsets.
138 if (type(v) == "table") then
139 postSet, preSet = v[2] or 0, v[3] or 0
140 v = v[1]
141 end
142
143 -- Get the length of the sound.
144 local length = SoundDuration(ADJUST_SOUND..v)
145 -- If the sound has a pause before it is played, add it here.
146 delay = delay + preSet
147
148 -- Have the sound play in the future.
149 timer.Simple(delay, function()
150 -- Check if the entity still exists and play the sound.
151 if (IsValid(entity)) then
152 entity:EmitSound(v, volume, pitch)
153 end
154 end)
155
156 -- Add the delay for the next sound.
157 delay = delay + length + postSet + spacing
158 end
159
160 -- Return how long it took for the whole thing.
161 return delay
162end
163
164function nut.util.gridVector(vec, gridSize)
165 if (gridSize <= 0) then
166 gridSize = 1
167 end
168
169 for i = 1, 3 do
170 vec[i] = vec[i] / gridSize
171 vec[i] = math.Round(vec[i])
172 vec[i] = vec[i] * gridSize
173 end
174
175 return vec
176end
177
178function nut.util.getAllChar()
179 local charTable = {}
180
181 for k, v in ipairs(player.GetAll()) do
182 if (v:getChar()) then
183 table.insert(charTable, v:getChar():getID())
184 end
185 end
186
187 return charTable
188end
189
190if (CLIENT) then
191 NUT_CVAR_CHEAP = CreateClientConVar("nut_cheapblur", 0, true)
192
193 local useCheapBlur = NUT_CVAR_CHEAP:GetBool()
194 local blur = nut.util.getMaterial("pp/blurscreen")
195
196 cvars.AddChangeCallback("nut_cheapblur", function(name, old, new)
197 useCheapBlur = (tonumber(new) or 0) > 0
198 end)
199
200 -- Draws a blurred material over the screen, to blur things.
201 function nut.util.drawBlur(panel, amount, passes)
202 -- Intensity of the blur.
203 amount = amount or 5
204
205 if (useCheapBlur) then
206 surface.SetDrawColor(50, 50, 50, amount * 20)
207 surface.DrawRect(0, 0, panel:GetWide(), panel:GetTall())
208 else
209 surface.SetMaterial(blur)
210 surface.SetDrawColor(255, 255, 255)
211
212 local x, y = panel:LocalToScreen(0, 0)
213
214 for i = -(passes or 0.2), 1, 0.2 do
215 -- Do things to the blur material to make it blurry.
216 blur:SetFloat("$blur", i * amount)
217 blur:Recompute()
218
219 -- Draw the blur material over the screen.
220 render.UpdateScreenEffectTexture()
221 surface.DrawTexturedRect(x * -1, y * -1, ScrW(), ScrH())
222 end
223 end
224 end
225
226 function nut.util.drawBlurAt(x, y, w, h, amount, passes)
227 -- Intensity of the blur.
228 amount = amount or 5
229
230 if (useCheapBlur) then
231 surface.SetDrawColor(30, 30, 30, amount * 20)
232 surface.DrawRect(x, y, w, h)
233 else
234 surface.SetMaterial(blur)
235 surface.SetDrawColor(255, 255, 255)
236
237 local scrW, scrH = ScrW(), ScrH()
238 local x2, y2 = x / scrW, y / scrH
239 local w2, h2 = (x + w) / scrW, (y + h) / scrH
240
241 for i = -(passes or 0.2), 1, 0.2 do
242 blur:SetFloat("$blur", i * amount)
243 blur:Recompute()
244
245 render.UpdateScreenEffectTexture()
246 surface.DrawTexturedRectUV(x, y, w, h, x2, y2, w2, h2)
247 end
248 end
249 end
250
251 -- Draw a text with a shadow.
252 function nut.util.drawText(text, x, y, color, alignX, alignY, font, alpha)
253 color = color or color_white
254
255 return draw.TextShadow({
256 text = text,
257 font = font or "nutGenericFont",
258 pos = {x, y},
259 color = color,
260 xalign = alignX or 0,
261 yalign = alignY or 0
262 }, 1, alpha or (color.a * 0.575))
263 end
264
265 -- Wraps text so it does not pass a certain width.
266 function nut.util.wrapText(text, width, font)
267 font = font or "nutChatFont"
268 surface.SetFont(font)
269
270 local exploded = string.Explode("%s", text, true)
271 local line = ""
272 local lines = {}
273 local w = surface.GetTextSize(text)
274 local maxW = 0
275
276 if (w <= width) then
277 return {(text:gsub("%s", " "))}, w
278 end
279
280 for i = 1, #exploded do
281 local word = exploded[i]
282 line = line.." "..word
283 w = surface.GetTextSize(line)
284
285 if (w > width) then
286 lines[#lines + 1] = line
287 line = ""
288
289 if (w > maxW) then
290 maxW = w
291 end
292 end
293 end
294
295 if (line != "") then
296 lines[#lines + 1] = line
297 end
298
299 return lines, maxW
300 end
301
302 local LAST_WIDTH = ScrW()
303 local LAST_HEIGHT = ScrH()
304
305 timer.Create("nutResolutionMonitor", 1, 0, function()
306 local scrW, scrH = ScrW(), ScrH()
307
308 if (scrW != LAST_WIDTH or scrH != LAST_HEIGHT) then
309 hook.Run("ScreenResolutionChanged", LAST_WIDTH, LAST_HEIGHT)
310
311 LAST_WIDTH = scrW
312 LAST_HEIGHT = scrH
313 end
314 end)
315end
316
317-- Utility entity extensions.
318do
319 local entityMeta = FindMetaTable("Entity")
320
321 -- Checks if an entity is a door by comparing its class.
322 function entityMeta:isDoor()
323 return self:GetClass():find("door")
324 end
325
326 -- Make a cache of chairs on start.
327 local CHAIR_CACHE = {}
328
329 -- Add chair models to the cache by checking if its vehicle category is a class.
330 for k, v in pairs(list.Get("Vehicles")) do
331 if (v.Category == "Chairs") then
332 CHAIR_CACHE[v.Model] = true
333 end
334 end
335
336 -- Whether or not a vehicle is a chair by checking its model with the chair list.
337 function entityMeta:isChair()
338 -- Micro-optimization in-case this gets used a lot.
339 return CHAIR_CACHE[self.GetModel(self)]
340 end
341
342 if (SERVER) then
343 -- Returns the door's slave entity.
344 function entityMeta:getDoorPartner()
345 return self.nutPartner
346 end
347
348 -- Returns whether door/button is locked or not.
349 function entityMeta:isLocked()
350 if (self:IsVehicle()) then
351 local datatable = self:GetSaveTable()
352
353 if (datatable) then
354 return (datatable.VehicleLocked)
355 end
356 else
357 local datatable = self:GetSaveTable()
358
359 if (datatable) then
360 return (datatable.m_bLocked)
361 end
362 end
363
364 return
365 end
366
367 -- Returns the entity that blocking door's sequence.
368 function entityMeta:getBlocker()
369 local datatable = self:GetSaveTable()
370
371 return (datatable.pBlocker)
372 end
373 else
374 -- Returns the door's slave entity.
375 function entityMeta:getDoorPartner()
376 local owner = self:GetOwner() or self.nutDoorOwner
377
378 if (IsValid(owner) and owner:isDoor()) then
379 return owner
380 end
381
382 for k, v in ipairs(ents.FindByClass("prop_door_rotating")) do
383 if (v:GetOwner() == self) then
384 self.nutDoorOwner = v
385
386 return v
387 end
388 end
389 end
390 end
391
392 -- Makes a fake door to replace it.
393 function entityMeta:blastDoor(velocity, lifeTime, ignorePartner)
394 if (!self:isDoor()) then
395 return
396 end
397
398 if (IsValid(self.nutDummy)) then
399 self.nutDummy:Remove()
400 end
401
402 velocity = velocity or VectorRand()*100
403 lifeTime = lifeTime or 120
404
405 local partner = self:getDoorPartner()
406
407 if (IsValid(partner) and !ignorePartner) then
408 partner:blastDoor(velocity, lifeTime, true)
409 end
410
411 local color = self:GetColor()
412
413 local dummy = ents.Create("prop_physics")
414 dummy:SetModel(self:GetModel())
415 dummy:SetPos(self:GetPos())
416 dummy:SetAngles(self:GetAngles())
417 dummy:Spawn()
418 dummy:SetColor(color)
419 dummy:SetMaterial(self:GetMaterial())
420 dummy:SetSkin(self:GetSkin() or 0)
421 dummy:SetRenderMode(RENDERMODE_TRANSALPHA)
422 dummy:CallOnRemove("restoreDoor", function()
423 if (IsValid(self)) then
424 self:SetNotSolid(false)
425 self:SetNoDraw(false)
426 self:DrawShadow(true)
427 self.ignoreUse = false
428 self.nutIsMuted = false
429
430 for k, v in ipairs(ents.GetAll()) do
431 if (v:GetParent() == self) then
432 v:SetNotSolid(false)
433 v:SetNoDraw(false)
434
435 if (v.onDoorRestored) then
436 v:onDoorRestored(self)
437 end
438 end
439 end
440 end
441 end)
442 dummy:SetOwner(self)
443 dummy:SetCollisionGroup(COLLISION_GROUP_WEAPON)
444
445 self:Fire("unlock")
446 self:Fire("open")
447 self:SetNotSolid(true)
448 self:SetNoDraw(true)
449 self:DrawShadow(false)
450 self.ignoreUse = true
451 self.nutDummy = dummy
452 self.nutIsMuted = true
453 self:DeleteOnRemove(dummy)
454
455 for k, v in ipairs(self:GetBodyGroups()) do
456 dummy:SetBodygroup(v.id, self:GetBodygroup(v.id))
457 end
458
459 for k, v in ipairs(ents.GetAll()) do
460 if (v:GetParent() == self) then
461 v:SetNotSolid(true)
462 v:SetNoDraw(true)
463
464 if (v.onDoorBlasted) then
465 v:onDoorBlasted(self)
466 end
467 end
468 end
469
470 dummy:GetPhysicsObject():SetVelocity(velocity)
471
472 local uniqueID = "doorRestore"..self:EntIndex()
473 local uniqueID2 = "doorOpener"..self:EntIndex()
474
475 timer.Create(uniqueID2, 1, 0, function()
476 if (IsValid(self) and IsValid(self.nutDummy)) then
477 self:Fire("open")
478 else
479 timer.Remove(uniqueID2)
480 end
481 end)
482
483 timer.Create(uniqueID, lifeTime, 1, function()
484 if (IsValid(self) and IsValid(dummy)) then
485 uniqueID = "dummyFade"..dummy:EntIndex()
486 local alpha = 255
487
488 timer.Create(uniqueID, 0.1, 255, function()
489 if (IsValid(dummy)) then
490 alpha = alpha - 1
491 dummy:SetColor(ColorAlpha(color, alpha))
492
493 if (alpha <= 0) then
494 dummy:Remove()
495 end
496 else
497 timer.Remove(uniqueID)
498 end
499 end)
500 end
501 end)
502
503 return dummy
504 end
505end
506
507-- Misc. player stuff.
508do
509 local playerMeta = FindMetaTable("Player")
510 ALWAYS_RAISED = {}
511 ALWAYS_RAISED["weapon_physgun"] = true
512 ALWAYS_RAISED["gmod_tool"] = true
513 ALWAYS_RAISED["nut_poshelper"] = true
514
515 -- Returns how many seconds the player has played on the server in total.
516 if (SERVER) then
517 function playerMeta:getPlayTime()
518 return self.nutPlayTime + (RealTime() - (self.nutJoinTime or RealTime()))
519 end
520 else
521 nut.playTime = nut.playTime or 0
522
523 function playerMeta:getPlayTime()
524 return nut.playTime + (RealTime() - nut.joinTime or 0)
525 end
526 end
527
528 -- Returns whether or not the player has their weapon raised.
529 function playerMeta:isWepRaised()
530 local weapon = self.GetActiveWeapon(self)
531 local override = hook.Run("ShouldWeaponBeRaised", self, weapon)
532
533 -- Allow the hook to check first.
534 if (override != nil) then
535 return override
536 end
537
538 -- Some weapons may have their own properties.
539 if (IsValid(weapon)) then
540 -- If their weapon is always raised, return true.
541 if (weapon.IsAlwaysRaised or ALWAYS_RAISED[weapon.GetClass(weapon)]) then
542 return true
543 -- Return false if always lowered.
544 elseif (weapon.IsAlwaysLowered or weapon.NeverRaised) then
545 return false
546 end
547 end
548
549 -- If the player has been forced to have their weapon lowered.
550 if (self.getNetVar(self, "restricted")) then
551 return false
552 end
553
554 -- Let the config decide before actual results.
555 if (nut.config.get("wepAlwaysRaised")) then
556 return true
557 end
558
559 -- Returns what the gamemode decides.
560 return self.getNetVar(self, "raised", false)
561 end
562
563 local vectorLength2D = FindMetaTable("Vector").Length2D
564
565 -- Checks if the player is running by seeing if the speed is faster than walking.
566 function playerMeta:isRunning()
567 return vectorLength2D(self.GetVelocity(self)) > (self.GetWalkSpeed(self) + 10)
568 end
569
570 -- Checks if the player has a female model.
571 function playerMeta:isFemale()
572 local model = self:GetModel():lower()
573
574 return model:find("female") or model:find("alyx") or model:find("mossman") or nut.anim.getModelClass(model) == "citizen_female"
575 end
576
577 -- Returns a good position in front of the player for an entity.
578 function playerMeta:getItemDropPos()
579 -- Start a trace.
580 local data = {}
581 data.start = self:GetShootPos()
582 data.endpos = self:GetShootPos() + self:GetAimVector()*86
583 data.filter = self
584 local trace = util.TraceLine(data)
585 data.start = trace.HitPos
586 data.endpos = data.start + trace.HitNormal*46
587 data.filter = {}
588 trace = util.TraceLine(data)
589
590 return trace.HitPos
591 end
592
593 -- Do an action that requires the player to stare at something.
594 function playerMeta:doStaredAction(entity, callback, time, onCancel, distance)
595 local uniqueID = "nutStare"..self:UniqueID()
596 local data = {}
597 data.filter = self
598
599 timer.Create(uniqueID, 0.1, time / 0.1, function()
600 if (IsValid(self) and IsValid(entity)) then
601 data.start = self:GetShootPos()
602 data.endpos = data.start + self:GetAimVector()*(distance or 96)
603
604 if (util.TraceLine(data).Entity != entity) then
605 timer.Remove(uniqueID)
606
607 if (onCancel) then
608 onCancel()
609 end
610 elseif (callback and timer.RepsLeft(uniqueID) == 0) then
611 callback()
612 end
613 else
614 timer.Remove(uniqueID)
615
616 if (onCancel) then
617 onCancel()
618 end
619 end
620 end)
621 end
622
623 if (SERVER) then
624 -- Sets whether or not the weapon is raised.
625 function playerMeta:setWepRaised(state)
626 -- Sets the networked variable for being raised.
627 self:setNetVar("raised", state)
628
629 -- Delays any weapon shooting.
630 local weapon = self:GetActiveWeapon()
631
632 if (IsValid(weapon)) then
633 weapon:SetNextPrimaryFire(CurTime() + 1)
634 weapon:SetNextSecondaryFire(CurTime() + 1)
635 end
636 end
637
638 -- Inverts whether or not the weapon is raised.
639 function playerMeta:toggleWepRaised()
640 self:setWepRaised(!self:isWepRaised())
641
642 local weapon = self:GetActiveWeapon()
643
644 if (IsValid(weapon)) then
645 if (self:isWepRaised() and weapon.OnRaised) then
646 weapon:OnRaised()
647 elseif (!self:isWepRaised() and weapon.OnLowered) then
648 weapon:OnLowered()
649 end
650 end
651 end
652
653 -- Performs a delayed action on a player.
654 function playerMeta:setAction(text, time, callback, startTime, finishTime)
655 if (time and time <= 0) then
656 if (callback) then
657 callback(self)
658 end
659
660 return
661 end
662
663 -- Default the time to five seconds.
664 time = time or 5
665 startTime = startTime or CurTime()
666 finishTime = finishTime or (startTime + time)
667
668 if (text == false) then
669 timer.Remove("nutAct"..self:UniqueID())
670 netstream.Start(self, "actBar")
671
672 return
673 end
674
675 -- Tell the player to draw a bar for the action.
676 netstream.Start(self, "actBar", startTime, finishTime, text)
677
678 -- If we have provided a callback, run it delayed.
679 if (callback) then
680 -- Create a timer that runs once with a delay.
681 timer.Create("nutAct"..self:UniqueID(), time, 1, function()
682 -- Call the callback if the player is still valid.
683 if (IsValid(self)) then
684 callback(self)
685 end
686 end)
687 end
688 end
689
690 -- Sends a Derma string request to the client.
691 function playerMeta:requestString(title, subTitle, callback, default)
692 local time = math.floor(os.time())
693
694 self.nutStrReqs = self.nutStrReqs or {}
695 self.nutStrReqs[time] = callback
696
697 netstream.Start(self, "strReq", time, title, subTitle, default)
698 end
699
700 -- Removes a player's weapon and restricts interactivity.
701 function playerMeta:setRestricted(state, noMessage)
702 if (state) then
703 self:setNetVar("restricted", true)
704
705 if (noMessage) then
706 self:setLocalVar("restrictNoMsg", true)
707 end
708
709 self.nutRestrictWeps = self.nutRestrictWeps or {}
710
711 for k, v in ipairs(self:GetWeapons()) do
712 self.nutRestrictWeps[#self.nutRestrictWeps + 1] = v:GetClass()
713 v:Remove()
714 end
715
716 hook.Run("OnPlayerRestricted", self)
717 else
718 self:setNetVar("restricted")
719
720 if (self:getLocalVar("restrictNoMsg")) then
721 self:setLocalVar("restrictNoMsg")
722 end
723
724 if (self.nutRestrictWeps) then
725 for k, v in ipairs(self.nutRestrictWeps) do
726 self:Give(v)
727 end
728
729 self.nutRestrictWeps = nil
730 end
731
732 hook.Run("OnPlayerUnRestricted", self)
733 end
734 end
735 end
736
737 -- Player ragdoll utility stuff.
738 do
739 function nut.util.findEmptySpace(entity, filter, spacing, size, height, tolerance)
740 spacing = spacing or 32
741 size = size or 3
742 height = height or 36
743 tolerance = tolerance or 5
744
745 local position = entity:GetPos()
746 local angles = Angle(0, 0, 0)
747 local mins, maxs = Vector(-spacing * 0.5, -spacing * 0.5, 0), Vector(spacing * 0.5, spacing * 0.5, height)
748 local output = {}
749
750 for x = -size, size do
751 for y = -size, size do
752 local origin = position + Vector(x * spacing, y * spacing, 0)
753 local color = green
754 local i = 0
755
756 local data = {}
757 data.start = origin + mins + Vector(0, 0, tolerance)
758 data.endpos = origin + maxs
759 data.filter = filter or entity
760 local trace = util.TraceLine(data)
761
762 data.start = origin + Vector(-maxs.x, -maxs.y, tolerance)
763 data.endpos = origin + Vector(mins.x, mins.y, height)
764
765 local trace2 = util.TraceLine(data)
766
767 if (trace.StartSolid or trace.Hit or trace2.StartSolid or trace2.Hit or !util.IsInWorld(origin)) then
768 continue
769 end
770
771 output[#output + 1] = origin
772 end
773 end
774
775 table.sort(output, function(a, b)
776 return a:Distance(position) < b:Distance(position)
777 end)
778
779 return output
780 end
781
782 function playerMeta:isStuck()
783 return util.TraceEntity({
784 start = self:GetPos(),
785 endpos = self:GetPos(),
786 filter = self
787 }, self).StartSolid
788 end
789
790 function playerMeta:setRagdolled(state, time, getUpGrace)
791 getUpGrace = getUpGrace or time or 5
792
793 if (state) then
794 if (IsValid(self.nutRagdoll)) then
795 self.nutRagdoll:Remove()
796 end
797
798 local entity = ents.Create("prop_ragdoll")
799 entity:SetPos(self:GetPos())
800 entity:SetAngles(self:EyeAngles())
801 entity:SetModel(self:GetModel())
802 entity:SetSkin(self:GetSkin())
803 entity:Spawn()
804 entity:setNetVar("player", self)
805 entity:SetCollisionGroup(COLLISION_GROUP_WEAPON)
806 entity:Activate()
807 entity:CallOnRemove("fixer", function()
808 if (IsValid(self)) then
809 self:setLocalVar("blur", nil)
810 self:setLocalVar("ragdoll", nil)
811
812 if (!entity.nutNoReset) then
813 self:SetPos(entity:GetPos())
814 end
815
816 self:SetNoDraw(false)
817 self:SetNotSolid(false)
818 self:Freeze(false)
819 self:SetMoveType(MOVETYPE_WALK)
820 self:SetLocalVelocity(IsValid(entity) and entity.nutLastVelocity or vector_origin)
821 end
822
823 if (IsValid(self) and !entity.nutIgnoreDelete) then
824 if (entity.nutWeapons) then
825 for k, v in ipairs(entity.nutWeapons) do
826 self:Give(v)
827 if (entity.nutAmmo) then
828 for k2, v2 in ipairs(entity.nutAmmo) do
829 if v == v2[1] then
830 self:SetAmmo(v2[2], tostring(k2))
831 end
832 end
833 end
834 end
835 for k, v in ipairs(self:GetWeapons()) do
836 v:SetClip1(0)
837 end
838 end
839
840 if (self:isStuck()) then
841 entity:DropToFloor()
842 self:SetPos(entity:GetPos() + Vector(0, 0, 16))
843
844 local positions = nut.util.findEmptySpace(self, {entity, self})
845
846 for k, v in ipairs(positions) do
847 self:SetPos(v)
848
849 if (!self:isStuck()) then
850 return
851 end
852 end
853 end
854 end
855 end)
856
857 local velocity = self:GetVelocity()
858
859 for i = 0, entity:GetPhysicsObjectCount() - 1 do
860 local physObj = entity:GetPhysicsObjectNum(i)
861
862 if (IsValid(physObj)) then
863 physObj:SetVelocity(velocity)
864
865 local index = entity:TranslatePhysBoneToBone(i)
866
867 if (index) then
868 local position, angles = self:GetBonePosition(index)
869
870 physObj:SetPos(position)
871 physObj:SetAngles(angles)
872 end
873 end
874 end
875
876 self:setLocalVar("blur", 25)
877 self.nutRagdoll = entity
878
879 entity.nutWeapons = {}
880 entity.nutAmmo = {}
881 entity.nutPlayer = self
882
883 if (getUpGrace) then
884 entity.nutGrace = CurTime() + getUpGrace
885 end
886
887 if (time and time > 0) then
888 entity.nutStart = CurTime()
889 entity.nutFinish = entity.nutStart + time
890
891 self:setAction("@wakingUp", nil, nil, entity.nutStart, entity.nutFinish)
892 end
893
894 for k, v in ipairs(self:GetWeapons()) do
895 entity.nutWeapons[#entity.nutWeapons + 1] = v:GetClass()
896 local clip = v:Clip1()
897 local reserve = self:GetAmmoCount(v:GetPrimaryAmmoType())
898 local ammo = clip + reserve
899 entity.nutAmmo[v:GetPrimaryAmmoType()] = {v:GetClass(), ammo}
900 end
901
902 self:GodDisable()
903 self:StripWeapons()
904 self:Freeze(true)
905 self:SetNoDraw(true)
906 self:SetNotSolid(true)
907
908 if (time) then
909 local time2 = time
910 local uniqueID = "nutUnRagdoll"..self:SteamID()
911
912 timer.Create(uniqueID, 0.33, 0, function()
913 if (IsValid(entity) and IsValid(self)) then
914 local velocity = entity:GetVelocity()
915 entity.nutLastVelocity = velocity
916
917 self:SetPos(entity:GetPos())
918
919 if (velocity:Length2D() >= 8) then
920 if (!entity.nutPausing) then
921 self:setAction()
922 entity.nutPausing = true
923 end
924
925 return
926 elseif (entity.nutPausing) then
927 self:setAction("@wakingUp", time)
928 entity.nutPausing = false
929 end
930
931 time = time - 0.33
932
933 if (time <= 0) then
934 entity:Remove()
935 end
936 else
937 timer.Remove(uniqueID)
938 end
939 end)
940 end
941
942 self:setLocalVar("ragdoll", entity:EntIndex())
943 hook.Run("OnCharFallover", self, entity, true)
944 elseif (IsValid(self.nutRagdoll)) then
945 self.nutRagdoll:Remove()
946
947 hook.Run("OnCharFallover", self, entity, false)
948 end
949 end
950 end
951end
952
953-- Time related stuff.
954do
955 -- Gets the current time in the UTC time-zone.
956 function nut.util.getUTCTime()
957 local date = os.date("!*t")
958 local localDate = os.date("*t")
959 localDate.isdst = false
960
961 return os.difftime(os.time(date), os.time(localDate))
962 end
963
964 -- Setup for time strings.
965 local TIME_UNITS = {}
966 TIME_UNITS["s"] = 1 -- Seconds
967 TIME_UNITS["m"] = 60 -- Minutes
968 TIME_UNITS["h"] = 3600 -- Hours
969 TIME_UNITS["d"] = TIME_UNITS["h"] * 24 -- Days
970 TIME_UNITS["w"] = TIME_UNITS["d"] * 7 -- Weeks
971 TIME_UNITS["mo"] = TIME_UNITS["d"] * 30 -- Months
972 TIME_UNITS["y"] = TIME_UNITS["d"] * 365 -- Years
973
974 -- Gets the amount of seconds from a given formatted string.
975 -- Example: 5y2d7w = 5 years, 2 days, and 7 weeks.
976 -- If just given a minute, it is assumed minutes.
977 function nut.util.getStringTime(text)
978 local minutes = tonumber(text)
979
980 if (minutes) then
981 return math.abs(minutes * 60)
982 end
983
984 local time = 0
985
986 for amount, unit in text:lower():gmatch("(%d+)(%a+)") do
987 amount = tonumber(amount)
988
989 if (amount and TIME_UNITS[unit]) then
990 time = time + math.abs(amount * TIME_UNITS[unit])
991 end
992 end
993
994 return time
995 end
996end