· 9 years ago · Oct 15, 2016, 10:42 PM
1--[[
2 © 2015 CloudSixteen.com do not share, re-distribute or modify
3 without permission of its author (kurozael@gmail.com).
4
5 Clockwork was created by Conna Wiles (also known as kurozael.)
6 http://cloudsixteen.com/license/clockwork.html
7--]]
8
9--[[ Micro-optimizations --]]
10local Clockwork = Clockwork;
11local von = von;
12local HITGROUP_RIGHTARM = HITGROUP_RIGHTARM;
13local HITGROUP_RIGHTLEG = HITGROUP_RIGHTLEG;
14local HITGROUP_LEFTARM = HITGROUP_LEFTARM;
15local HITGROUP_LEFTLEG = HITGROUP_LEFTLEG;
16local HITGROUP_STOMACH = HITGROUP_STOMACH;
17local HITGROUP_CHEST = HITGROUP_CHEST;
18local HITGROUP_HEAD = HITGROUP_HEAD;
19local UnPredictedCurTime = UnPredictedCurTime;
20local RunConsoleCommand = RunConsoleCommand;
21local FindMetaTable = FindMetaTable;
22local getmetatable = getmetatable;
23local setmetatable = setmetatable;
24local GetGlobalVar = GetGlobalVar;
25local SetGlobalVar = SetGlobalVar;
26local ErrorNoHalt = ErrorNoHalt;
27local EffectData = EffectData;
28local VectorRand = VectorRand;
29local DamageInfo = DamageInfo;
30local tonumber = tonumber;
31local tostring = tostring;
32local CurTime = CurTime;
33local IsValid = IsValid;
34local SysTime = SysTime;
35local unpack = unpack;
36local Format = Format;
37local Vector = Vector;
38local Color = Color;
39local pairs = pairs;
40local pcall = pcall;
41local type = type;
42local resource = resource;
43local string = string;
44local table = table;
45local timer = timer;
46local ents = ents;
47local hook = hook;
48local math = math;
49local util = util;
50
51--[[ Math Library Localizations --]]
52local mathNormalizeAngle = math.NormalizeAngle;
53local mathApproach = math.Approach;
54local mathRandom = math.random;
55local mathRound = math.Round;
56local mathClamp = math.Clamp;
57local mathFloor = math.floor;
58local mathCeil = math.ceil;
59local mathSin = math.sin;
60local mathMin = math.min;
61local mathMax = math.max;
62local mathAbs = math.abs;
63
64--[[ String Library Localizations --]]
65local stringExplode = string.Explode;
66local stringFormat = string.format;
67local stringGmatch = string.gmatch;
68local stringSub = string.utf8sub;
69local stringLen = string.utf8len;
70local stringLower = string.lower;
71local stringUpper = string.upper;
72local stringMatch = string.match;
73local stringFind = string.find;
74local stringGsub = string.gsub;
75local stringByte = string.byte;
76local stringRep = string.rep;
77
78--[[ Table Library Localizations --]]
79local tableHasValue = table.HasValue;
80local tableInsert = table.insert;
81local tableRemove = table.remove;
82local tableCount = table.Count;
83local tableSort = table.sort;
84local tableAdd = table.Add;
85
86Clockwork.kernel = Clockwork.kernel or {};
87Clockwork.Timers = Clockwork.Timers or {};
88Clockwork.Libraries = Clockwork.Libraries or {};
89Clockwork.SharedTables = Clockwork.SharedTables or {};
90
91--[[
92 @codebase Shared
93 @details A function to encode a URL.
94 @param String The URL to encode.
95 @returns String The encoded URL.
96--]]
97function Clockwork.kernel:URLEncode(url)
98 local output = "";
99
100 for i = 1, #url do
101 local c = stringSub(url, i, i);
102 local a = stringByte(c);
103
104 if (a < 128) then
105 if (a == 32 or a >= 34 and a <= 38 or a == 43 or a == 44 or a == 47 or a >= 58
106 and a <= 64 or a >= 91 and a <= 94 or a == 96 or a >= 123 and a <= 126) then
107 output = output.."%"..stringFormat("%x", a);
108 else
109 output = output..c;
110 end;
111 end;
112 end;
113
114 return output;
115end;
116
117--[[
118 @codebase Shared
119 @details A function to get whether two tables are equal.
120 @param Table The first unique table to compare.
121 @param Table The second unique table to compare.
122 @returns Bool Whether or not the tables are equal.
123--]]
124function Clockwork.kernel:AreTablesEqual(tableA, tableB)
125 if (type(tableA) == "table" and type(tableB) == "table") then
126 for k, v in pairs(tableA) do
127 if (!self:AreTablesEqual(v, tableB[k])) then
128 return false;
129 end;
130 end;
131
132 return true;
133 end;
134
135 return (tableA == tableB);
136end;
137
138--[[
139 @codebase Shared
140 @details A function to get whether a weapon is a default weapon.
141 @param Entity The weapon entity.
142 @returns Bool Whether or not the weapon is a default weapon.
143--]]
144function Clockwork.kernel:IsDefaultWeapon(weapon)
145 if (IsValid(weapon)) then
146 local class = stringLower(weapon:GetClass());
147 if (class == "weapon_physgun" or class == "gmod_physcannon"
148 or class == "gmod_tool") then
149 return true;
150 end;
151 end;
152
153 return false;
154end;
155
156-- A function to format cash.
157function Clockwork.kernel:FormatCash(amount, singular, lowerName)
158 local formatSingular = Clockwork.option:GetKey("format_singular_cash");
159 local formatCash = Clockwork.option:GetKey("format_cash");
160 local cashName = Clockwork.option:GetKey("name_cash", lowerName);
161 local realAmount = tostring(mathRound(amount));
162
163 if (singular) then
164 return self:Replace(self:Replace(formatSingular, "%n", cashName), "%a", realAmount);
165 else
166 return self:Replace(self:Replace(formatCash, "%n", cashName), "%a", realAmount);
167 end;
168end;
169
170--[[
171 Define the default library class.
172--]]
173
174local LIBRARY = {};
175
176-- A function to add a library function to a metatable.
177function LIBRARY:AddToMetaTable(metaName, funcName, newName)
178 local metaTable = FindMetaTable(metaName);
179
180 metaTable[newName or funcName] = function(...)
181 return self[funcName](self, ...)
182 end;
183end;
184
185-- A function to create a new library.
186function Clockwork.kernel:NewLibrary(libName)
187 if (!Clockwork.Libraries[libName]) then
188 Clockwork.Libraries[libName] = self:NewMetaTable(LIBRARY);
189 end;
190
191 return Clockwork.Libraries[libName];
192end;
193
194-- A function find a library by its name.
195function Clockwork.kernel:FindLibrary(libName)
196 return Clockwork.Libraries[libName];
197end;
198
199-- A function to create a library if it doesn't exist, and then return it.
200function cwLibrary(libName)
201 if (!Clockwork[libName]) then
202 Clockwork[libName] = Clockwork.kernel:NewLibrary(libName);
203 end;
204
205 return Clockwork[libName];
206end;
207
208-- An alias for the above function.
209function cwLib(libName)
210 return library(libName);
211end;
212
213-- A function to create a library and class function.
214function cwClass(name)
215 local lib = cwLib(name);
216 local CLASS = {__index = CLASS};
217
218 function lib:New(...)
219 local obj = Clockwork.kernel:NewMetaTable(CLASS);
220 obj = lib[name](obj, ...);
221 return obj, lib;
222 end;
223end;
224
225-- A function to convert a string to a color.
226function Clockwork.kernel:StringToColor(text)
227 local explodedData = stringExplode(",", text);
228 local color = Color(255, 255, 255, 255);
229
230 if (explodedData[1]) then
231 color.r = tonumber(explodedData[1]:Trim()) or 255;
232 end;
233
234 if (explodedData[2]) then
235 color.g = tonumber(explodedData[2]:Trim()) or 255;
236 end;
237
238 if (explodedData[3]) then
239 color.b = tonumber(explodedData[3]:Trim()) or 255;
240 end;
241
242 if (explodedData[4]) then
243 color.a = tonumber(explodedData[4]:Trim()) or 255;
244 end;
245
246 return color;
247end;
248
249-- A function to get a log type color.
250function Clockwork.kernel:GetLogTypeColor(logType)
251 local logTypes = {
252 Color(255, 50, 50, 255),
253 Color(255, 150, 0, 255),
254 Color(255, 200, 0, 255),
255 Color(0, 150, 255, 255),
256 Color(0, 255, 125, 255)
257 };
258
259 return logTypes[logType] or logTypes[5];
260end;
261
262--[[
263 @codebase Shared
264 @details A function to get the kernel version.
265 @returns String The kernel version.
266--]]
267function Clockwork.kernel:GetVersion()
268 return Clockwork.KernelVersion;
269end;
270
271--[[
272 @codebase Shared
273 @details A function to get the kernel build.
274 @returns String The kernel build.
275--]]
276function Clockwork.kernel:GetBuild()
277 return Clockwork.KernelBuild;
278end;
279
280--[[
281 @codebase Shared
282 @details A function to get the kernel version and build.
283 @returns String The kernel version and build concatenated.
284--]]
285function Clockwork.kernel:GetVersionBuild()
286 if (Clockwork.KernelBuild) then
287 return Clockwork.KernelVersion.."-"..Clockwork.KernelBuild;
288 else
289 return Clockwork.KernelVersion;
290 end;
291end;
292
293--[[
294 @codebase Shared
295 @details A function to get the schema folder.
296 @returns String The schema folder.
297--]]
298function Clockwork.kernel:GetSchemaFolder(sFolderName)
299 if (sFolderName) then
300 return (stringGsub(Clockwork.SchemaFolder, "gamemodes/", "").."/schema/"..sFolderName);
301 else
302 return (stringGsub(Clockwork.SchemaFolder, "gamemodes/", ""));
303 end;
304end;
305
306--[[
307 @codebase Shared
308 @details A function to get the schema gamemode path.
309 @returns String The schema gamemode path.
310--]]
311function Clockwork.kernel:GetSchemaGamemodePath()
312 return (stringGsub(Clockwork.SchemaFolder, "gamemodes/", "").."/gamemode");
313end;
314
315--[[
316 @codebase Shared
317 @details A function to get the Clockwork folder.
318 @returns String The Clockwork folder.
319--]]
320function Clockwork.kernel:GetClockworkFolder()
321 return (stringGsub(Clockwork.ClockworkFolder, "gamemodes/", ""));
322end;
323
324--[[
325 @codebase Shared
326 @details A function to get the Clockwork path.
327 @returns String The Clockwork path.
328--]]
329function Clockwork.kernel:GetClockworkPath()
330 return (stringGsub(Clockwork.ClockworkFolder, "gamemodes/", "").."/framework");
331end;
332
333-- A function to get the path to GMod.
334function Clockwork.kernel:GetPathToGMod()
335 return util.RelativePathToFull("."):sub(1, -2);
336end;
337
338-- A function to convert a string to a boolean.
339function Clockwork.kernel:ToBool(text)
340 if (text == "true" or text == "yes" or text == "1") then
341 return true;
342 else
343 return false;
344 end;
345end;
346
347-- A function to remove text from the end of a string.
348function Clockwork.kernel:RemoveTextFromEnd(text, toRemove)
349 local toRemoveLen = stringLen(toRemove);
350 if (stringSub(text, -toRemoveLen) == toRemove) then
351 return (stringSub(text, 0, -(toRemoveLen + 1)));
352 else
353 return text;
354 end;
355end;
356
357-- A function to split a string.
358function Clockwork.kernel:SplitString(text, interval)
359 local length = stringLen(text);
360 local baseTable = {};
361 local i = 0;
362
363 while (i * interval < length) do
364 baseTable[i + 1] = stringSub(text, i * interval + 1, (i + 1) * interval);
365 i = i + 1;
366 end;
367
368 return baseTable;
369end;
370
371-- A function to get whether a letter is a vowel.
372function Clockwork.kernel:IsVowel(letter)
373 letter = stringLower(letter);
374 return (letter == "a" or letter == "e" or letter == "i"
375 or letter == "o" or letter == "u");
376end;
377
378-- A function to pluralize some text.
379function Clockwork.kernel:Pluralize(text)
380 if (stringSub(text, -2) != "fe") then
381 local lastLetter = stringSub(text, -1);
382
383 if (lastLetter == "y") then
384 if (self:IsVowel(stringSub(text, stringLen(text) - 1, 2))) then
385 return stringSub(text, 1, -2).."ies";
386 else
387 return text.."s";
388 end;
389 elseif (lastLetter == "h") then
390 return text.."es";
391 elseif (lastLetter != "s") then
392 return text.."s";
393 else
394 return text;
395 end;
396 else
397 return stringSub(text, 1, -3).."ves";
398 end;
399end;
400
401-- A function to serialize a table.
402function Clockwork.kernel:Serialize(tableToSerialize)
403 local bSuccess, value = pcall(von.serialize, tableToSerialize);
404
405 if (!bSuccess) then
406 print(value);
407 return "";
408 end;
409
410 return value;
411end;
412
413-- A function to deserialize a string.
414function Clockwork.kernel:Deserialize(stringToDeserialize)
415 local bSuccess, value = pcall(von.deserialize, stringToDeserialize);
416
417 if (!bSuccess) then
418 print(value);
419 return {};
420 end;
421
422 return value;
423end;
424
425-- A function to get ammo information from a weapon.
426function Clockwork.kernel:GetAmmoInformation(weapon)
427 if (IsValid(weapon) and IsValid(weapon.Owner) and weapon.Primary and weapon.Secondary) then
428 if (!weapon.AmmoInfo) then
429 weapon.AmmoInfo = {
430 primary = {
431 ammoType = weapon:GetPrimaryAmmoType(),
432 clipSize = weapon.Primary.ClipSize
433 },
434 secondary = {
435 ammoType = weapon:GetSecondaryAmmoType(),
436 clipSize = weapon.Secondary.ClipSize
437 }
438 };
439 end;
440
441 weapon.AmmoInfo.primary.ownerAmmo = weapon.Owner:GetAmmoCount(weapon.AmmoInfo.primary.ammoType);
442 weapon.AmmoInfo.primary.clipBullets = weapon:Clip1();
443 weapon.AmmoInfo.primary.doesNotShoot = (weapon.AmmoInfo.primary.clipBullets == -1);
444 weapon.AmmoInfo.secondary.ownerAmmo = weapon.Owner:GetAmmoCount(weapon.AmmoInfo.secondary.ammoType);
445 weapon.AmmoInfo.secondary.clipBullets = weapon:Clip2();
446 weapon.AmmoInfo.secondary.doesNotShoot = (weapon.AmmoInfo.secondary.clipBullets == -1);
447
448 if (!weapon.AmmoInfo.primary.doesNotShoot and weapon.AmmoInfo.primary.ownerAmmo > 0) then
449 weapon.AmmoInfo.primary.ownerClips = mathCeil(weapon.AmmoInfo.primary.clipSize / weapon.AmmoInfo.primary.ownerAmmo);
450 else
451 weapon.AmmoInfo.primary.ownerClips = 0;
452 end;
453
454 if (!weapon.AmmoInfo.secondary.doesNotShoot and weapon.AmmoInfo.secondary.ownerAmmo > 0) then
455 weapon.AmmoInfo.secondary.ownerClips = mathCeil(weapon.AmmoInfo.secondary.clipSize / weapon.AmmoInfo.secondary.ownerAmmo);
456 else
457 weapon.AmmoInfo.secondary.ownerClips = 0;
458 end;
459
460 return weapon.AmmoInfo;
461 end;
462end;
463
464-- Called when a player's footstep sound should be played.
465function Clockwork:PlayerFootstep(player, position, foot, sound, volume, recipientFilter)
466 if (CLIENT) then return true; end;
467
468 if (!self.plugin:Call("PrePlayerDefaultFootstep", player, position, foot, sound, volume, recipientFilter)) then
469 local itemTable = player:GetClothesItem();
470
471 if (itemTable) then
472 if ( player:IsRunning() or player:IsJogging() ) then
473 if (itemTable.runSound) then
474 if (type(itemTable.runSound) == "table") then
475 sound = itemTable.runSound[ mathRandom(1, #itemTable.runSound) ];
476 else
477 sound = itemTable.runSound;
478 end;
479 end;
480 elseif (itemTable.walkSound) then
481 if (type(itemTable.walkSound) == "table") then
482 sound = itemTable.walkSound[ mathRandom(1, #itemTable.walkSound) ];
483 else
484 sound = itemTable.walkSound;
485 end;
486 end;
487 end;
488
489 player:EmitSound(sound);
490
491 return true;
492 end;
493end;
494
495-- Called when the player's jumping animation should be handled.
496function Clockwork:HandlePlayerJumping(player)
497 if (!player.m_bJumping and !player:OnGround() and player:WaterLevel() <= 0) then
498 player.m_bJumping = true;
499 player.m_bFirstJumpFrame = false;
500 player.m_flJumpStartTime = 0;
501 end
502
503 if (player.m_bJumping) then
504 if (player.m_bFirstJumpFrame) then
505 player.m_bFirstJumpFrame = false;
506 player:AnimRestartMainSequence();
507 end;
508
509 if (player:WaterLevel() >= 2) then
510 player.m_bJumping = false;
511 player:AnimRestartMainSequence();
512 elseif (CurTime() - player.m_flJumpStartTime > 0.2) then
513 if (player:OnGround()) then
514 player.m_bJumping = false;
515 player:AnimRestartMainSequence();
516 end
517 end
518
519 if (player.m_bJumping) then
520 player.CalcIdeal = Clockwork.animation:GetForModel(player:GetModel(), "jump");
521
522 return true;
523 end;
524 end;
525
526 return false;
527end;
528
529-- Called when the player's ducking animation should be handled.
530function Clockwork:HandlePlayerDucking(player, velocity)
531 if (player:Crouching()) then
532 local model = player:GetModel();
533 local weapon = player:GetActiveWeapon();
534 local bIsRaised = Clockwork.player:GetWeaponRaised(player, true);
535 local velLength = velocity:Length2D();
536 local animationAct = "crouch";
537 local weaponHoldType = "pistol";
538
539 if (IsValid(weapon)) then
540 weaponHoldType = Clockwork.animation:GetWeaponHoldType(player, weapon);
541
542 if (weaponHoldType) then
543 animationAct = animationAct.."_"..weaponHoldType;
544 end;
545 end;
546
547 if (bIsRaised) then
548 animationAct = animationAct.."_aim";
549 end;
550
551 if (velLength > 0.5) then
552 animationAct = animationAct.."_walk";
553 else
554 animationAct = animationAct.."_idle";
555 end;
556
557 player.CalcIdeal = Clockwork.animation:GetForModel(model, animationAct);
558
559 return true;
560 end;
561
562 return false;
563end;
564
565-- Called when the player's swimming animation should be handled.
566function Clockwork:HandlePlayerSwimming(player)
567 if (player:WaterLevel() >= 2) then
568 if (player.m_bFirstSwimFrame) then
569 player:AnimRestartMainSequence();
570 player.m_bFirstSwimFrame = false;
571 end;
572
573 player.m_bInSwim = true;
574 else
575 player.m_bInSwim = false;
576
577 if (!player.m_bFirstSwimFrame) then
578 player.m_bFirstSwimFrame = true;
579 end;
580 end;
581
582 return false;
583end;
584
585-- Called when the player's driving animation should be handled.
586function Clockwork:HandlePlayerDriving(player)
587 if (player:InVehicle()) then
588 player.CalcIdeal = Clockwork.animation:GetForModel(player:GetModel(), "sit");
589 return true;
590 end;
591
592 return false;
593end;
594
595-- Called when a player's animation is updated.
596function Clockwork:UpdateAnimation(player, velocity, maxSeqGroundSpeed)
597 local velLength = velocity:Length2D();
598 local rate = 1.0;
599
600 if (velLength > 0.5) then
601 rate = ((velLength * 0.8) / maxSeqGroundSpeed);
602 end
603
604 player.cwPlaybackRate = mathClamp(rate, 0, 1.5);
605 player:SetPlaybackRate(player.cwPlaybackRate);
606
607 if (player:InVehicle() and CLIENT) then
608 local vehicle = player:GetVehicle();
609
610 if (IsValid(vehicle)) then
611 local velocity = vehicle:GetVelocity();
612 local steer = (vehicle:GetPoseParameter("vehicle_steer") * 2) - 1;
613
614 player:SetPoseParameter("vertical_velocity", velocity.z * 0.01);
615 player:SetPoseParameter("vehicle_steer", steer);
616 end;
617 end;
618end;
619
620local IdleActivity = ACT_HL2MP_IDLE;
621local IdleActivityTranslate = {
622 [ACT_MP_ATTACK_CROUCH_PRIMARYFIRE] = IdleActivity + 5,
623 [ACT_MP_ATTACK_STAND_PRIMARYFIRE] = IdleActivity + 5,
624 [ACT_MP_RELOAD_CROUCH] = IdleActivity + 6,
625 [ACT_MP_RELOAD_STAND] = IdleActivity + 6,
626 [ACT_MP_CROUCH_IDLE] = IdleActivity + 3,
627 [ACT_MP_STAND_IDLE] = IdleActivity,
628 [ACT_MP_CROUCHWALK] = IdleActivity + 4,
629 [ACT_MP_JUMP] = ACT_HL2MP_JUMP_SLAM,
630 [ACT_MP_WALK] = IdleActivity + 1,
631 [ACT_MP_RUN] = IdleActivity + 2,
632};
633
634-- Called when a player's activity is supposed to be translated.
635function Clockwork:TranslateActivity(player, act)
636 local model = player:GetModel();
637 local bIsRaised = Clockwork.player:GetWeaponRaised(player, true);
638
639 if (stringFind(model, "/player/")) then
640 local newAct = player:TranslateWeaponActivity(act);
641
642 if (!bIsRaised or act == newAct) then
643 return IdleActivityTranslate[act];
644 else
645 return newAct;
646 end;
647 end;
648
649 return act;
650end;
651
652-- Called when the main activity should be calculated.
653function Clockwork:CalcMainActivity(player, velocity)
654 local model = player:GetModel();
655
656 ANIMATION_PLAYER = player;
657
658 local weapon = player:GetActiveWeapon();
659 local bIsRaised = Clockwork.player:GetWeaponRaised(player, true);
660 local animationAct = "stand";
661 local weaponHoldType = "pistol";
662 local forcedAnimation = player:GetForcedAnimation();
663
664 if (IsValid(weapon)) then
665 weaponHoldType = Clockwork.animation:GetWeaponHoldType(player, weapon);
666
667 if (weaponHoldType) then
668 animationAct = animationAct.."_"..weaponHoldType;
669 end;
670 end;
671
672 if (bIsRaised) then
673 animationAct = animationAct.."_aim";
674 end;
675
676 player.CalcIdeal = Clockwork.animation:GetForModel(model, animationAct.."_idle");
677 player.CalcSeqOverride = -1;
678
679 if (!self:HandlePlayerDriving(player)
680 and !self:HandlePlayerJumping(player)
681 and !self:HandlePlayerDucking(player, velocity)
682 and !self:HandlePlayerSwimming(player)
683 and !self:HandlePlayerNoClipping(player, velocity)
684 and !self:HandlePlayerVaulting(player, velocity)) then
685 local velLength = velocity:Length2D();
686
687 if (player:IsRunning() or player:IsJogging()) then
688 player.CalcIdeal = Clockwork.animation:GetForModel(model, animationAct.."_run");
689 elseif (velLength > 0.5) then
690 player.CalcIdeal = Clockwork.animation:GetForModel(model, animationAct.."_walk");
691 end;
692
693 if (CLIENT) then
694 player:SetIK(false);
695 end;
696 end;
697
698 if (forcedAnimation) then
699 player.CalcSeqOverride = forcedAnimation.animation;
700
701 if (forcedAnimation.OnAnimate) then
702 forcedAnimation.OnAnimate(player);
703 forcedAnimation.OnAnimate = nil;
704 end;
705 end;
706
707 if (type(player.CalcSeqOverride) == "string") then
708 player.CalcSeqOverride = player:LookupSequence(player.CalcSeqOverride);
709 end;
710
711 if (type(player.CalcIdeal) == "string") then
712 player.CalcSeqOverride = player:LookupSequence(player.CalcIdeal);
713 end;
714
715 ANIMATION_PLAYER = nil;
716
717 local eyeAngles = player:EyeAngles();
718 local yaw = velocity:Angle().yaw;
719 local normalized = mathNormalizeAngle(yaw - eyeAngles.y);
720
721 player:SetPoseParameter("move_yaw", normalized);
722
723 return player.CalcIdeal, player.CalcSeqOverride;
724end;
725
726-- Called when the animation event is supposed to be done.
727function Clockwork:DoAnimationEvent(player, event, data)
728 local model = player:GetModel();
729
730 if (stringFind(model, "/player/")) then
731 return self.BaseClass:DoAnimationEvent(player, event, data);
732 end;
733
734 local weapon = player:GetActiveWeapon();
735 local animationAct = "pistol";
736
737 if (IsValid(weapon)) then
738 weaponHoldType = Clockwork.animation:GetWeaponHoldType(player, weapon);
739
740 if (weaponHoldType) then
741 animationAct = weaponHoldType;
742 end;
743 end;
744
745 if (event == PLAYERANIMEVENT_ATTACK_PRIMARY) then
746 local gestureSequence = Clockwork.animation:GetForModel(model, animationAct.."_attack");
747
748 if (gestureSequence) then
749 if (player:Crouching()) then
750 player:AnimRestartGesture(GESTURE_SLOT_ATTACK_AND_RELOAD, gestureSequence, true);
751 else
752 player:AnimRestartGesture(GESTURE_SLOT_ATTACK_AND_RELOAD, gestureSequence, true);
753 end;
754 end;
755
756 return ACT_VM_PRIMARYATTACK;
757 elseif (event == PLAYERANIMEVENT_RELOAD) then
758 local gestureSequence = Clockwork.animation:GetForModel(model, animationAct.."_reload");
759
760 if (gestureSequence) then
761 if (player:Crouching()) then
762 player:AnimRestartGesture(GESTURE_SLOT_ATTACK_AND_RELOAD, gestureSequence, true);
763 else
764 player:AnimRestartGesture(GESTURE_SLOT_ATTACK_AND_RELOAD, gestureSequence, true);
765 end;
766 end;
767
768 return ACT_INVALID;
769 elseif (event == PLAYERANIMEVENT_JUMP) then
770 player.m_bJumping = true;
771 player.m_bFirstJumpFrame = true;
772 player.m_flJumpStartTime = CurTime();
773
774 player:AnimRestartMainSequence();
775
776 return ACT_INVALID;
777 elseif (event == PLAYERANIMEVENT_CANCEL_RELOAD) then
778 player:AnimResetGestureSlot(GESTURE_SLOT_ATTACK_AND_RELOAD);
779
780 return ACT_INVALID;
781 end;
782
783 return nil;
784end;
785
786if (SERVER) then
787 local ServerLog = ServerLog;
788 local cvars = cvars;
789
790 Clockwork.Entities = {};
791 Clockwork.TempPlayerData = {};
792 Clockwork.HitGroupBonesCache = {
793 {"ValveBiped.Bip01_R_UpperArm", HITGROUP_RIGHTARM},
794 {"ValveBiped.Bip01_R_Forearm", HITGROUP_RIGHTARM},
795 {"ValveBiped.Bip01_L_UpperArm", HITGROUP_LEFTARM},
796 {"ValveBiped.Bip01_L_Forearm", HITGROUP_LEFTARM},
797 {"ValveBiped.Bip01_R_Thigh", HITGROUP_RIGHTLEG},
798 {"ValveBiped.Bip01_R_Calf", HITGROUP_RIGHTLEG},
799 {"ValveBiped.Bip01_R_Foot", HITGROUP_RIGHTLEG},
800 {"ValveBiped.Bip01_R_Hand", HITGROUP_RIGHTARM},
801 {"ValveBiped.Bip01_L_Thigh", HITGROUP_LEFTLEG},
802 {"ValveBiped.Bip01_L_Calf", HITGROUP_LEFTLEG},
803 {"ValveBiped.Bip01_L_Foot", HITGROUP_LEFTLEG},
804 {"ValveBiped.Bip01_L_Hand", HITGROUP_LEFTARM},
805 {"ValveBiped.Bip01_Pelvis", HITGROUP_STOMACH},
806 {"ValveBiped.Bip01_Spine2", HITGROUP_CHEST},
807 {"ValveBiped.Bip01_Spine1", HITGROUP_CHEST},
808 {"ValveBiped.Bip01_Head1", HITGROUP_HEAD},
809 {"ValveBiped.Bip01_Neck1", HITGROUP_HEAD}
810 };
811 Clockwork.MeleeTranslation = {
812 [ACT_HL2MP_GESTURE_RANGE_ATTACK] = ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE2,
813 [ACT_HL2MP_GESTURE_RELOAD] = ACT_HL2MP_GESTURE_RELOAD_MELEE2,
814 [ACT_HL2MP_WALK_CROUCH] = ACT_HL2MP_WALK_CROUCH_MELEE2,
815 [ACT_HL2MP_IDLE_CROUCH] = ACT_HL2MP_IDLE_CROUCH_MELEE2,
816 [ACT_RANGE_ATTACK1] = ACT_RANGE_ATTACK1_MELEE2,
817 [ACT_HL2MP_IDLE] = ACT_HL2MP_IDLE_MELEE2,
818 [ACT_HL2MP_WALK] = ACT_HL2MP_WALK_MELEE2,
819 [ACT_HL2MP_JUMP] = ACT_HL2MP_JUMP_MELEE2,
820 [ACT_HL2MP_RUN] = ACT_HL2MP_RUN_MELEE2
821 };
822
823 -- A function to save schema data.
824 function Clockwork.kernel:SaveSchemaData(fileName, data)
825 if (type(data) != "table") then
826 MsgC(Color(255, 100, 0, 255), "[Clockwork:Kernel] The '"..fileName.."' schema data has failed to save.\nUnable to save type "..type(data)..", table required.\n");
827 return;
828 end;
829
830 return Clockwork.file:Write("settings/clockwork/schemas/"..self:GetSchemaFolder().."/"..fileName..".cw", self:Serialize(data));
831 end;
832
833 -- A function to delete schema data.
834 function Clockwork.kernel:DeleteSchemaData(fileName)
835 return Clockwork.file:Delete("settings/clockwork/schemas/"..self:GetSchemaFolder().."/"..fileName..".cw");
836 end;
837
838 -- A function to check if schema data exists.
839 function Clockwork.kernel:SchemaDataExists(fileName)
840 return _file.Exists("settings/clockwork/schemas/"..self:GetSchemaFolder().."/"..fileName..".cw", "GAME");
841 end;
842
843 -- A function to get the schema data path.
844 function Clockwork.kernel:GetSchemaDataPath()
845 return "settings/clockwork/schemas/"..self:GetSchemaFolder();
846 end;
847
848 local SCHEMA_GAMEMODE_INFO = nil;
849
850 -- A function to get the schema gamemode info.
851 function Clockwork.kernel:GetSchemaGamemodeInfo()
852 if (SCHEMA_GAMEMODE_INFO) then return SCHEMA_GAMEMODE_INFO; end;
853
854 local schemaFolder = stringLower(self:GetSchemaFolder());
855 local schemaData = util.KeyValuesToTable(
856 Clockwork.file:Read("gamemodes/"..schemaFolder.."/"..schemaFolder..".txt")
857 );
858
859 if (not schemaData) then
860 schemaData = {};
861 end;
862
863 if (schemaData["Gamemode"]) then
864 schemaData = schemaData["Gamemode"];
865 end;
866
867 SCHEMA_GAMEMODE_INFO = {};
868 SCHEMA_GAMEMODE_INFO["name"] = schemaData["title"] or "Undefined";
869 SCHEMA_GAMEMODE_INFO["author"] = schemaData["author"] or "Undefined";
870 SCHEMA_GAMEMODE_INFO["description"] = schemaData["description"] or "Undefined";
871 SCHEMA_GAMEMODE_INFO["version"] = schemaData["version"] or "Undefined";
872 return SCHEMA_GAMEMODE_INFO;
873 end;
874
875 -- A function to get the schema gamemode name.
876 function Clockwork.kernel:GetSchemaGamemodeName()
877 local schemaInfo = self:GetSchemaGamemodeInfo();
878 return schemaInfo["name"];
879 end;
880
881 -- A function to get the schema version.
882 function Clockwork.kernel:GetSchemaGamemodeVersion()
883 local schemaInfo = self:GetSchemaGamemodeInfo();
884 return schemaInfo["version"];
885 end;
886
887 -- A function to find schema data in a directory.
888 function Clockwork.kernel:FindSchemaDataInDir(directory)
889 return _file.Find("settings/clockwork/schemas/"..self:GetSchemaFolder().."/"..directory, "GAME");
890 end;
891
892 -- A function to restore schema data.
893 function Clockwork.kernel:RestoreSchemaData(fileName, failSafe)
894 if (self:SchemaDataExists(fileName)) then
895 local data = Clockwork.file:Read("settings/clockwork/schemas/"..self:GetSchemaFolder().."/"..fileName..".cw", "namedesc");
896
897 if (data) then
898 local bSuccess, value = pcall(self.Deserialize, self, data);
899
900 if (bSuccess and value != nil) then
901 return value;
902 else
903 MsgC(Color(255, 100, 0, 255), "[Clockwork:Kernel] '"..fileName.."' schema data has failed to restore.\n"..value.."\n");
904
905 self:DeleteSchemaData(fileName);
906 end;
907 end;
908 end;
909
910 if (failSafe != nil) then
911 return failSafe;
912 else
913 return {};
914 end;
915 end;
916
917 -- A function to restore Clockwork data.
918 function Clockwork.kernel:RestoreClockworkData(fileName, failSafe)
919 if (self:ClockworkDataExists(fileName)) then
920 local data = Clockwork.file:Read("settings/clockwork/"..fileName..".cw");
921
922 if (data) then
923 local bSuccess, value = pcall(util.JSONToTable, data);
924
925 if (bSuccess and value != nil) then
926 return value;
927 else
928 local bSuccess, value = pcall(self.Deserialize, self, data);
929
930 if (bSuccess and value != nil) then
931 return value;
932 else
933 MsgC(Color(255, 100, 0, 255), "[Clockwork:Kernel] '"..fileName.."' clockwork data has failed to restore.\n"..value.."\n");
934
935 self:DeleteClockworkData(fileName);
936 end;
937 end;
938 end;
939 end;
940
941 if (failSafe != nil) then
942 return failSafe;
943 else
944 return {};
945 end;
946 end;
947
948 -- A function to setup a full directory.
949 function Clockwork.kernel:SetupFullDirectory(filePath)
950 local directory = stringGsub(self:GetPathToGMod()..filePath, "\\", "/");
951 local exploded = stringExplode("/", directory);
952 local currentPath = "";
953
954 for k, v in pairs(exploded) do
955 if (k < #exploded) then
956 currentPath = currentPath..v.."/";
957 Clockwork.file:MakeDirectory(currentPath);
958 end;
959 end;
960
961 return currentPath..exploded[#exploded];
962 end;
963
964 -- A function to save Clockwork data.
965 function Clockwork.kernel:SaveClockworkData(fileName, data)
966 if (type(data) != "table") then
967 MsgC(Color(255, 100, 0, 255), "[Clockwork:Kernel] The '"..fileName.."' clockwork data has failed to save.\nUnable to save type "..type(data)..", table required.\n");
968
969 return;
970 end;
971
972 return Clockwork.file:Write("settings/clockwork/"..fileName..".cw", self:Serialize(data));
973 end;
974
975 -- A function to check if Clockwork data exists.
976 function Clockwork.kernel:ClockworkDataExists(fileName)
977 return _file.Exists("settings/clockwork/"..fileName..".cw", "GAME");
978 end;
979
980 -- A function to delete Clockwork data.
981 function Clockwork.kernel:DeleteClockworkData(fileName)
982 return Clockwork.file:Delete("settings/clockwork/"..fileName..".cw");
983 end;
984
985 -- A function to convert a force.
986 function Clockwork.kernel:ConvertForce(force, limit)
987 local forceLength = force:Length();
988
989 if (forceLength == 0) then
990 return Vector(0, 0, 0);
991 end;
992
993 if (!limit) then
994 limit = 800;
995 end;
996
997 if (forceLength > limit) then
998 return force / (forceLength / limit);
999 else
1000 return force;
1001 end;
1002 end;
1003
1004 -- A function to save a player's attribute boosts.
1005 function Clockwork.kernel:SavePlayerAttributeBoosts(player, data)
1006 local attributeBoosts = player:GetAttributeBoosts();
1007 local curTime = CurTime();
1008
1009 if (data["AttrBoosts"]) then
1010 data["AttrBoosts"] = nil;
1011 end;
1012
1013 if (tableCount(attributeBoosts) > 0) then
1014 data["AttrBoosts"] = {};
1015
1016 for k, v in pairs(attributeBoosts) do
1017 data["AttrBoosts"][k] = {};
1018
1019 for k2, v2 in pairs(v) do
1020 if (v2.duration) then
1021 if (curTime < v2.endTime) then
1022 data["AttrBoosts"][k][k2] = {
1023 duration = mathCeil(v2.endTime - curTime),
1024 amount = v2.amount
1025 };
1026 end;
1027 else
1028 data["AttrBoosts"][k][k2] = {
1029 amount = v2.amount
1030 };
1031 end;
1032 end;
1033 end;
1034 end;
1035 end;
1036
1037 -- A function to calculate a player's spawn time.
1038 function Clockwork.kernel:CalculateSpawnTime(player, inflictor, attacker, damageInfo)
1039 local info = {
1040 attacker = attacker,
1041 inflictor = inflictor,
1042 spawnTime = Clockwork.config:Get("spawn_time"):Get(),
1043 damageInfo = damageInfo
1044 };
1045
1046 Clockwork.plugin:Call("PlayerAdjustDeathInfo", player, info);
1047
1048 if (info.spawnTime and info.spawnTime > 0) then
1049 Clockwork.player:SetAction(player, "spawn", info.spawnTime, 3);
1050 end;
1051 end;
1052
1053 -- A function to create a decal.
1054 function Clockwork.kernel:CreateDecal(texture, position, temporary)
1055 local decal = ents.Create("infodecal");
1056
1057 if (temporary) then
1058 decal:SetKeyValue("LowPriority", "true");
1059 end;
1060
1061 decal:SetKeyValue("Texture", texture);
1062 decal:SetPos(position);
1063 decal:Spawn();
1064 decal:Fire("activate");
1065
1066 return decal;
1067 end;
1068
1069 -- A function to handle a player's weapon fire delay.
1070 function Clockwork.kernel:HandleWeaponFireDelay(player, bIsRaised, weapon, curTime)
1071 local delaySecondaryFire = nil;
1072 local delayPrimaryFire = nil;
1073
1074 if (!Clockwork.plugin:Call("PlayerCanFireWeapon", player, bIsRaised, weapon, true)) then
1075 delaySecondaryFire = curTime + 60;
1076 end;
1077
1078 if (!Clockwork.plugin:Call("PlayerCanFireWeapon", player, bIsRaised, weapon)) then
1079 delayPrimaryFire = curTime + 60;
1080 end;
1081
1082 if (delaySecondaryFire == nil and weapon.secondaryFireDelayed) then
1083 weapon:SetNextSecondaryFire(weapon.secondaryFireDelayed);
1084 weapon.secondaryFireDelayed = nil;
1085 end;
1086
1087 if (delayPrimaryFire == nil and weapon.primaryFireDelayed) then
1088 weapon:SetNextPrimaryFire(weapon.primaryFireDelayed);
1089 weapon.primaryFireDelayed = nil;
1090 end;
1091
1092 if (delaySecondaryFire) then
1093 if (!weapon.secondaryFireDelayed) then
1094 weapon.secondaryFireDelayed = weapon:GetNextSecondaryFire();
1095 end;
1096
1097 --[[
1098 This is a terrible hotfix for the SMG not being able
1099 to fire after loading ammunition.
1100 --]]
1101 if (weapon:GetClass() != "weapon_smg1") then
1102 weapon:SetNextSecondaryFire(delaySecondaryFire);
1103 end;
1104 end;
1105
1106 if (delayPrimaryFire) then
1107 if (!weapon.primaryFireDelayed) then
1108 weapon.primaryFireDelayed = weapon:GetNextPrimaryFire();
1109 end;
1110
1111 weapon:SetNextPrimaryFire(delayPrimaryFire);
1112 end;
1113 end;
1114
1115 -- A function to scale damage by hit group.
1116 function Clockwork:ScaleDamageByHitGroup(player, attacker, hitGroup, damageInfo, baseDamage)
1117 if (!damageInfo:IsFallDamage() and !damageInfo:IsDamageType(DMG_CRUSH)) then
1118 if (hitGroup == HITGROUP_HEAD) then
1119 damageInfo:ScaleDamage(Clockwork.config:Get("scale_head_dmg"):Get());
1120 elseif (hitGroup == HITGROUP_CHEST or hitGroup == HITGROUP_GENERIC) then
1121 damageInfo:ScaleDamage(Clockwork.config:Get("scale_chest_dmg"):Get());
1122 elseif (hitGroup == HITGROUP_LEFTARM or hitGroup == HITGROUP_RIGHTARM or hitGroup == HITGROUP_LEFTLEG
1123 or hitGroup == HITGROUP_RIGHTLEG or hitGroup == HITGROUP_GEAR) then
1124 damageInfo:ScaleDamage(Clockwork.config:Get("scale_limb_dmg"):Get());
1125 end;
1126 end;
1127
1128 self.plugin:Call("PlayerScaleDamageByHitGroup", player, attacker, hitGroup, damageInfo, baseDamage);
1129 end;
1130
1131 -- A function to calculate player damage.
1132 function Clockwork.kernel:CalculatePlayerDamage(player, hitGroup, damageInfo)
1133 local bDamageIsValid = damageInfo:IsBulletDamage() or damageInfo:IsDamageType(DMG_CLUB) or damageInfo:IsDamageType(DMG_SLASH);
1134 local bHitGroupIsValid = true;
1135
1136 if (Clockwork.config:Get("armor_chest_only"):Get()) then
1137 if (hitGroup != HITGROUP_CHEST and hitGroup != HITGROUP_GENERIC) then
1138 bHitGroupIsValid = nil;
1139 end;
1140 end;
1141
1142 if (player:Armor() > 0 and bDamageIsValid and bHitGroupIsValid) then
1143 local armor = player:Armor() - damageInfo:GetDamage();
1144
1145 if (armor < 0) then
1146 Clockwork.limb:TakeDamage(player, hitGroup, damageInfo:GetDamage() * 2);
1147 player:SetHealth(mathMax(player:Health() - mathAbs(armor), 1));
1148 player:SetArmor(mathMax(armor, 0));
1149 else
1150 player:SetArmor(mathMax(armor, 0));
1151 end;
1152 else
1153 Clockwork.limb:TakeDamage(player, hitGroup, damageInfo:GetDamage() * 2);
1154 player:SetHealth(mathMax(player:Health() - damageInfo:GetDamage(), 1));
1155 end;
1156
1157 if (damageInfo:IsFallDamage()) then
1158 Clockwork.limb:TakeDamage(player, HITGROUP_RIGHTLEG, damageInfo:GetDamage());
1159 Clockwork.limb:TakeDamage(player, HITGROUP_LEFTLEG, damageInfo:GetDamage());
1160 end;
1161 end;
1162
1163 -- A function to get a ragdoll's hit bone.
1164 function Clockwork.kernel:GetRagdollHitBone(entity, position, failSafe, minimum)
1165 local closest = {};
1166
1167 for k, v in pairs(Clockwork.HitGroupBonesCache) do
1168 local bone = entity:LookupBone(v[1]);
1169
1170 if (bone) then
1171 local bonePosition = entity:GetBonePosition(bone);
1172
1173 if (bonePosition) then
1174 local distance = bonePosition:Distance(position);
1175
1176 if (!closest[1] or distance < closest[1]) then
1177 if (!minimum or distance <= minimum) then
1178 closest[1] = distance;
1179 closest[2] = bone;
1180 end;
1181 end;
1182 end;
1183 end;
1184 end;
1185
1186 if (closest[2]) then
1187 return closest[2];
1188 else
1189 return failSafe;
1190 end;
1191 end;
1192
1193 -- A function to get a ragdoll's hit group.
1194 function Clockwork.kernel:GetRagdollHitGroup(entity, position)
1195 local closest = {nil, HITGROUP_GENERIC};
1196
1197 for k, v in pairs(Clockwork.HitGroupBonesCache) do
1198 local bone = entity:LookupBone(v[1]);
1199
1200 if (bone) then
1201 local bonePosition = entity:GetBonePosition(bone);
1202
1203 if (position) then
1204 local distance = bonePosition:Distance(position);
1205
1206 if (!closest[1] or distance < closest[1]) then
1207 closest[1] = distance;
1208 closest[2] = v[2];
1209 end;
1210 end;
1211 end;
1212 end;
1213
1214 return closest[2];
1215 end;
1216
1217 -- A function to create blood effects at a position.
1218 function Clockwork.kernel:CreateBloodEffects(position, decals, entity, forceVec, fScale)
1219 if (!entity.cwNextBlood or CurTime() >= entity.cwNextBlood) then
1220 local effectData = EffectData();
1221 effectData:SetOrigin(position);
1222 effectData:SetNormal(forceVec or (VectorRand() * 80));
1223 effectData:SetScale(fScale or 0.5);
1224 util.Effect("cw_bloodsmoke", effectData, true, true);
1225
1226 local effectData = EffectData();
1227 effectData:SetOrigin(position);
1228 effectData:SetEntity(entity);
1229 effectData:SetStart(position);
1230 effectData:SetScale(fScale or 0.5);
1231 util.Effect("BloodImpact", effectData, true, true);
1232
1233 for i = 1, decals do
1234 local trace = {};
1235 trace.start = position;
1236 trace.endpos = trace.start;
1237 trace.filter = entity;
1238 trace = util.TraceLine(trace);
1239
1240 util.Decal("Blood", trace.HitPos + trace.HitNormal, trace.HitPos - trace.HitNormal);
1241 end;
1242
1243 entity.cwNextBlood = CurTime() + 0.5;
1244 end;
1245 end;
1246
1247 -- A function to do the entity take damage hook.
1248 function Clockwork.kernel:DoEntityTakeDamageHook(arguments)
1249 local entity = arguments[1];
1250 local damageInfo = arguments[2];
1251
1252 if (!IsValid(entity)) then
1253 return;
1254 end;
1255
1256 local inflictor = damageInfo:GetInflictor();
1257 local attacker = damageInfo:GetAttacker();
1258 local amount = damageInfo:GetDamage();
1259
1260 if (amount != damageInfo:GetDamage()) then
1261 amount = damageInfo:GetDamage();
1262 end;
1263
1264 local player = Clockwork.entity:GetPlayer(entity);
1265
1266 if (player) then
1267 local ragdoll = player:GetRagdollEntity();
1268
1269 hook.Call("PrePlayerTakeDamage", Clockwork, player, attacker, inflictor, damageInfo);
1270
1271 if (!hook.Call("PlayerShouldTakeDamage", Clockwork, player, attacker, inflictor, damageInfo)
1272 or player:IsInGodMode()) then
1273 damageInfo:SetDamage(0);
1274
1275 return true;
1276 end;
1277
1278 if (ragdoll and entity != ragdoll) then
1279 hook.Call("EntityTakeDamage", Clockwork, ragdoll, damageInfo);
1280 damageInfo:SetDamage(0);
1281
1282 return true;
1283 end;
1284
1285 if (entity == ragdoll) then
1286 local physicsObject = entity:GetPhysicsObject();
1287
1288 if (IsValid(physicsObject)) then
1289 local velocity = physicsObject:GetVelocity():Length();
1290 local curTime = CurTime();
1291
1292 if (damageInfo:IsDamageType(DMG_CRUSH)) then
1293 if (entity.cwNextFallDamage
1294 and curTime < entity.cwNextFallDamage) then
1295 damageInfo:SetDamage(0);
1296 return true;
1297 end;
1298
1299 amount = hook.Call("GetFallDamage", Clockwork, player, velocity);
1300 entity.cwNextFallDamage = curTime + 1;
1301 damageInfo:SetDamage(amount)
1302 end;
1303 end;
1304 end;
1305 end;
1306 end;
1307
1308 -- A function to perform the date and time think.
1309 function Clockwork.kernel:PerformDateTimeThink()
1310 local defaultDays = Clockwork.option:GetKey("default_days");
1311 local minute = Clockwork.time:GetMinute();
1312 local month = Clockwork.date:GetMonth();
1313 local year = Clockwork.date:GetYear();
1314 local hour = Clockwork.time:GetHour();
1315 local day = Clockwork.time:GetDay();
1316
1317 Clockwork.time.minute = Clockwork.time:GetMinute() + 1;
1318
1319 if (Clockwork.time:GetMinute() == 60) then
1320 Clockwork.time.minute = 0;
1321 Clockwork.time.hour = Clockwork.time:GetHour() + 1;
1322
1323 if (Clockwork.time:GetHour() == 24) then
1324 Clockwork.time.hour = 0;
1325 Clockwork.time.day = Clockwork.time:GetDay() + 1;
1326 Clockwork.date.day = Clockwork.date:GetDay() + 1;
1327
1328 if (Clockwork.time:GetDay() == #defaultDays + 1) then
1329 Clockwork.time.day = 1;
1330 end;
1331
1332 if (Clockwork.date:GetDay() == 31) then
1333 Clockwork.date.day = 1;
1334 Clockwork.date.month = Clockwork.date:GetMonth() + 1;
1335
1336 if (Clockwork.date:GetMonth() == 13) then
1337 Clockwork.date.month = 1;
1338 Clockwork.date.year = Clockwork.date:GetYear() + 1;
1339 end;
1340 end;
1341 end;
1342 end;
1343
1344 if (Clockwork.time:GetMinute() != minute) then
1345 Clockwork.plugin:Call("TimePassed", TIME_MINUTE);
1346 end;
1347
1348 if (Clockwork.time:GetHour() != hour) then
1349 Clockwork.plugin:Call("TimePassed", TIME_HOUR);
1350 end;
1351
1352 if (Clockwork.time:GetDay() != day) then
1353 Clockwork.plugin:Call("TimePassed", TIME_DAY);
1354 end;
1355
1356 if (Clockwork.date:GetMonth() != month) then
1357 Clockwork.plugin:Call("TimePassed", TIME_MONTH);
1358 end;
1359
1360 if (Clockwork.date:GetYear() != year) then
1361 Clockwork.plugin:Call("TimePassed", TIME_YEAR);
1362 end;
1363
1364 local month = self:ZeroNumberToDigits(Clockwork.date:GetMonth(), 2);
1365 local day = self:ZeroNumberToDigits(Clockwork.date:GetDay(), 2);
1366
1367 self:SetSharedVar("Minute", Clockwork.time:GetMinute());
1368 self:SetSharedVar("Hour", Clockwork.time:GetHour());
1369 self:SetSharedVar("Date", day.."/"..month.."/"..Clockwork.date:GetYear());
1370 self:SetSharedVar("Day", Clockwork.time:GetDay());
1371 end;
1372
1373 -- A function to create a ConVar.
1374 function Clockwork.kernel:CreateConVar(name, value, flags, Callback)
1375 local conVar = CreateConVar(name, value, flags or FCVAR_REPLICATED + FCVAR_NOTIFY + FCVAR_ARCHIVE);
1376
1377 cvars.AddChangeCallback(name, function(conVar, previousValue, newValue)
1378 Clockwork.plugin:Call("ClockworkConVarChanged", conVar, previousValue, newValue);
1379
1380 if (Callback) then
1381 Callback(conVar, previousValue, newValue);
1382 end;
1383 end);
1384
1385 return conVar;
1386 end;
1387
1388 -- A function to check if the server is shutting down.
1389 function Clockwork.kernel:IsShuttingDown()
1390 return Clockwork.ShuttingDown;
1391 end;
1392
1393 -- A function to distribute wages cash.
1394 function Clockwork.kernel:DistributeWagesCash()
1395 local plyTable = cwPlayer.GetAll();
1396
1397 for k, v in pairs(plyTable) do
1398 if (v:HasInitialized() and v:Alive()) then
1399 local info = {
1400 wages = v:GetWages();
1401 };
1402
1403 Clockwork.plugin:Call("PlayerModifyWagesInfo", v, info);
1404
1405 if (Clockwork.plugin:Call("PlayerCanEarnWagesCash", v, info.wages)) then
1406 if (info.wages > 0) then
1407 if (Clockwork.plugin:Call("PlayerGiveWagesCash", v, info.wages, v:GetWagesName())) then
1408 Clockwork.player:GiveCash(v, info.wages, v:GetWagesName());
1409 end;
1410 end;
1411
1412 Clockwork.plugin:Call("PlayerEarnWagesCash", v, info.wages);
1413 end;
1414 end;
1415 end;
1416 end;
1417
1418 -- A function to distribute generator cash.
1419 function Clockwork.kernel:DistributeGeneratorCash()
1420 local generatorEntities = {};
1421
1422 for k, v in pairs(Clockwork.generator:GetAll()) do
1423 tableAdd(generatorEntities, ents.FindByClass(k));
1424 end;
1425
1426 for k, v in pairs(generatorEntities) do
1427 local generator = Clockwork.generator:FindByID(v:GetClass());
1428 local player = v:GetPlayer();
1429
1430 if (IsValid(player) and v:GetPower() != 0) then
1431 local info = {
1432 generator = generator,
1433 entity = v,
1434 cash = generator.cash,
1435 name = "Generator"
1436 };
1437
1438 v:SetDTInt(0, mathMax(v:GetPower() - 1, 0));
1439 Clockwork.plugin:Call("PlayerAdjustEarnGeneratorInfo", player, info);
1440
1441 if (Clockwork.plugin:Call("PlayerCanEarnGeneratorCash", player, info, info.cash)) then
1442 if (v.OnEarned) then
1443 local result = v:OnEarned(player, info.cash);
1444
1445 if (type(result) == "number") then
1446 info.cash = result;
1447 end;
1448
1449 if (result != false) then
1450 if (result != true) then
1451 Clockwork.player:GiveCash(k, info.cash, info.name);
1452 end;
1453
1454 Clockwork.plugin:Call("PlayerEarnGeneratorCash", player, info, info.cash);
1455 end;
1456 else
1457 Clockwork.player:GiveCash(k, info.cash, info.name);
1458 Clockwork.plugin:Call("PlayerEarnGeneratorCash", player, info, info.cash);
1459 end;
1460 end;
1461 end;
1462 end;
1463 end;
1464
1465 -- A function to include the schema.
1466 function Clockwork.kernel:IncludeSchema()
1467 return CloudAuthX.kernel:IncludeSchema();
1468 end;
1469
1470 -- A function to print a log message.
1471 function Clockwork.kernel:PrintLog(logType, text)
1472 local listeners = {};
1473 local plyTable = cwPlayer.GetAll();
1474
1475 for k, v in pairs(plyTable) do
1476 if (v:HasInitialized() and v:GetInfoNum("cwShowLog", 0) == 1) then
1477 if (Clockwork.player:IsAdmin(v)) then
1478 listeners[#listeners + 1] = v;
1479 end;
1480 end;
1481 end;
1482
1483 Clockwork.datastream:Start(listeners, "Log", {
1484 logType = (logType or 5), text = text
1485 });
1486
1487 if (CW_CONVAR_LOG:GetInt() == 1 and game.IsDedicated()) then
1488 self:ServerLog(text);
1489 end;
1490 end;
1491
1492 -- A function to log to the server.
1493 function Clockwork.kernel:ServerLog(text)
1494 local dateInfo = os.date("*t");
1495 local unixTime = os.time();
1496
1497 if (dateInfo) then
1498 if (dateInfo.month < 10) then dateInfo.month = "0"..dateInfo.month; end;
1499 if (dateInfo.day < 10) then dateInfo.day = "0"..dateInfo.day; end;
1500 local fileName = dateInfo.year.."-"..dateInfo.month.."-"..dateInfo.day;
1501
1502 if (dateInfo.hour < 10) then dateInfo.hour = "0"..dateInfo.hour; end;
1503 if (dateInfo.min < 10) then dateInfo.min = "0"..dateInfo.min; end;
1504 if (dateInfo.sec < 10) then dateInfo.sec = "0"..dateInfo.sec; end;
1505 local time = dateInfo.hour..":"..dateInfo.min..":"..dateInfo.sec;
1506 local logText = time..": "..stringGsub(text, "\n", "");
1507
1508 Clockwork.file:Append("logs/clockwork/"..fileName..".log", logText.."\n");
1509 end;
1510
1511 ServerLog(text.."\n"); Clockwork.plugin:Call("ClockworkLog", text, unixTime);
1512 end;
1513else
1514 local CreateClientConVar = CreateClientConVar;
1515 local CloseDermaMenus = CloseDermaMenus;
1516 local ChangeTooltip = ChangeTooltip;
1517 local ScreenScale = ScreenScale;
1518 local FrameTime = FrameTime;
1519 local DermaMenu = DermaMenu;
1520 local ScrW = ScrW;
1521 local ScrH = ScrH;
1522 local surface = surface;
1523 local render = render;
1524 local draw = draw;
1525 local vgui = vgui;
1526 local cam = cam;
1527 local gui = gui;
1528
1529 Clockwork.BackgroundBlurs = Clockwork.BackgroundBlurs or {};
1530 Clockwork.RecognisedNames = Clockwork.RecognisedNames or {};
1531 Clockwork.NetworkProxies = Clockwork.NetworkProxies or {};
1532 Clockwork.AccessoryData = Clockwork.AccessoryData or {};
1533 Clockwork.InfoMenuOpen = false;
1534 Clockwork.ColorModify = Clockwork.ColorModify or {};
1535 Clockwork.ClothesData = Clockwork.ClothesData or {};
1536 Clockwork.Cinematics = Clockwork.Cinematics or {};
1537
1538 Clockwork.kernel.CenterHints = Clockwork.kernel.CenterHints or {};
1539 Clockwork.kernel.ESPInfo = Clockwork.kernel.ESPInfo or {};
1540 Clockwork.kernel.Hints = Clockwork.kernel.Hints or {};
1541
1542 -- A function to register a network proxy.
1543 function Clockwork.kernel:RegisterNetworkProxy(entity, name, Callback)
1544 if (!Clockwork.NetworkProxies[entity]) then
1545 Clockwork.NetworkProxies[entity] = {};
1546 end;
1547
1548 Clockwork.NetworkProxies[entity][name] = {
1549 Callback = Callback,
1550 oldValue = nil
1551 };
1552 end;
1553
1554 -- A function to get whether the info menu is open.
1555 function Clockwork.kernel:IsInfoMenuOpen()
1556 return Clockwork.InfoMenuOpen;
1557 end;
1558
1559 -- A function to create a client ConVar.
1560 function Clockwork.kernel:CreateClientConVar(name, value, save, userData, Callback)
1561 local conVar = CreateClientConVar(name, value, save, userData);
1562
1563 cvars.AddChangeCallback(name, function(conVar, previousValue, newValue)
1564 Clockwork.plugin:Call("ClockworkConVarChanged", conVar, previousValue, newValue);
1565
1566 if (Callback) then
1567 Callback(conVar, previousValue, newValue);
1568 end;
1569 end);
1570
1571 return conVar;
1572 end;
1573
1574 -- A function to scale a font size to the screen.
1575 function Clockwork.kernel:FontScreenScale(size)
1576 --[[
1577 This will be the new method.
1578 return size * (ScrH() / 480.0);
1579 --]]
1580
1581 return ScreenScale(size);
1582 end;
1583
1584 -- A function to get a material.
1585 function Clockwork.kernel:GetMaterial(materialPath, pngParameters)
1586 self.CachedMaterial = self.CachedMaterial or {};
1587
1588 if (!self.CachedMaterial[materialPath]) then
1589 self.CachedMaterial[materialPath] = Material(materialPath, pngParameters);
1590 end;
1591
1592 return self.CachedMaterial[materialPath];
1593 end;
1594
1595 -- A function to get the 3D font size.
1596 function Clockwork.kernel:GetFontSize3D()
1597 return self:FontScreenScale(32);
1598 end;
1599
1600 -- A function to get the size of text.
1601 function Clockwork.kernel:GetTextSize(font, text)
1602 local defaultWidth, defaultHeight = self:GetCachedTextSize(font, "U");
1603 local height = defaultHeight;
1604 local width = 0;
1605 local textLength = 0;
1606
1607 for i in stringGmatch(text, "([%z\1-\127\194-\244][\128-\191]*)") do
1608 local currentCharacter = textLength + 1;
1609 local textWidth, textHeight = self:GetCachedTextSize(font, stringSub(text, currentCharacter, currentCharacter));
1610
1611 if (textWidth == 0) then
1612 textWidth = defaultWidth;
1613 end;
1614
1615 if (textHeight > height) then
1616 height = textHeight;
1617 end;
1618
1619 width = width + textWidth;
1620 textLength = textLength + 1;
1621 end;
1622
1623 return width, height;
1624 end;
1625
1626 -- A function to calculate alpha from a distance.
1627 function Clockwork.kernel:CalculateAlphaFromDistance(maximum, start, finish)
1628 if (type(start) == "Player") then
1629 start = start:GetShootPos();
1630 elseif (type(start) == "Entity") then
1631 start = start:GetPos();
1632 end;
1633
1634 if (type(finish) == "Player") then
1635 finish = finish:GetShootPos();
1636 elseif (type(finish) == "Entity") then
1637 finish = finish:GetPos();
1638 end;
1639
1640 return mathClamp(255 - ((255 / maximum) * (start:Distance(finish))), 0, 255);
1641 end;
1642
1643 -- A function to wrap text into a table.
1644 function Clockwork.kernel:WrapText(text, font, maximumWidth, baseTable)
1645 if (maximumWidth <= 0 or !text or text == "") then
1646 return;
1647 end;
1648
1649 if (self:GetTextSize(font, text) > maximumWidth) then
1650 local currentWidth = 0;
1651 local firstText = nil;
1652 local secondText = nil;
1653
1654 for i = 0, #text do
1655 local currentCharacter = stringSub(text, i, i);
1656 local currentSingleWidth = Clockwork.kernel:GetTextSize(font, currentCharacter);
1657
1658 if ((currentWidth + currentSingleWidth) >= maximumWidth) then
1659 baseTable[#baseTable + 1] = stringSub(text, 0, (i - 1));
1660 text = stringSub(text, i);
1661
1662 break;
1663 else
1664 currentWidth = currentWidth + currentSingleWidth;
1665 end;
1666 end;
1667
1668 if (self:GetTextSize(font, text) > maximumWidth) then
1669 self:WrapText(text, font, maximumWidth, baseTable);
1670 else
1671 baseTable[#baseTable + 1] = text;
1672 end;
1673 else
1674 baseTable[#baseTable + 1] = text;
1675 end;
1676 end;
1677
1678 -- A function to handle an entity's menu.
1679 function Clockwork.kernel:HandleEntityMenu(entity)
1680 local options = {};
1681 local itemTable = nil;
1682
1683 Clockwork.plugin:Call("GetEntityMenuOptions", entity, options);
1684
1685 if (entity:GetClass() == "cw_item") then
1686 itemTable = entity:GetItemTable();
1687 if (itemTable and itemTable:IsInstance() and itemTable.GetOptions) then
1688 local itemOptions = itemTable:GetOptions(entity);
1689
1690 for k, v in pairs(itemOptions) do
1691 options[k] = {
1692 title = k,
1693 name = v,
1694 isOptionTable = true,
1695 isArgTable = true
1696 };
1697 end;
1698 end;
1699 end;
1700
1701 if (tableCount(options) == 0) then return; end;
1702
1703 local menuPanel = self:AddMenuFromData(nil, options, function(menuPanel, option, arguments)
1704 if (itemTable and type(arguments) == "table" and arguments.isOptionTable) then
1705 menuPanel:AddOption(arguments.title, function()
1706 if (itemTable.HandleOptions) then
1707 local transmit, data = itemTable:HandleOptions(arguments.name, nil, nil, entity);
1708
1709 if (transmit) then
1710 Clockwork.datastream:Start("MenuOption", {
1711 option = arguments.name,
1712 data = data,
1713 item = itemTable("itemID"),
1714 entity = entity
1715 });
1716 end;
1717 end;
1718 end)
1719 else
1720 menuPanel:AddOption(option, function()
1721 if (type(arguments) == "table" and arguments.isArgTable) then
1722 if (arguments.Callback) then
1723 arguments.Callback(function(arguments)
1724 Clockwork.entity:ForceMenuOption(
1725 entity, option, arguments
1726 );
1727 end);
1728 else
1729 Clockwork.entity:ForceMenuOption(
1730 entity, option, arguments.arguments
1731 );
1732 end;
1733 else
1734 Clockwork.entity:ForceMenuOption(
1735 entity, option, arguments
1736 );
1737 end;
1738
1739 timer.Simple(FrameTime(), function()
1740 self:RemoveActiveToolTip();
1741 end);
1742 end);
1743 end;
1744
1745 menuPanel.Items = menuPanel:GetChildren();
1746 local panel = menuPanel.Items[#menuPanel.Items];
1747
1748 if (IsValid(panel)) then
1749 if (type(arguments) == "table") then
1750 if (arguments.isOrdered) then
1751 menuPanel.Items[#menuPanel.Items] = nil;
1752 tableInsert(menuPanel.Items, 1, panel);
1753 end;
1754
1755 if (arguments.toolTip) then
1756 self:CreateMarkupToolTip(panel);
1757 panel:SetMarkupToolTip(arguments.toolTip);
1758 end;
1759 end;
1760 end;
1761 end);
1762
1763 self:RegisterBackgroundBlur(menuPanel, SysTime());
1764 self:SetTitledMenu(menuPanel, "INTERACT WITH THIS ENTITY");
1765 menuPanel.entity = entity;
1766
1767 return menuPanel;
1768 end;
1769
1770 -- A function to get the gradient texture.
1771 function Clockwork.kernel:GetGradientTexture()
1772 return Clockwork.GradientTexture;
1773 end;
1774
1775 -- A function to add a menu from data.
1776 function Clockwork.kernel:AddMenuFromData(menuPanel, data, Callback, iMinimumWidth, bManualOpen)
1777 local bCreated = false;
1778 local options = {};
1779
1780 if (!menuPanel) then
1781 bCreated = true; menuPanel = DermaMenu();
1782
1783 if (iMinimumWidth) then
1784 menuPanel:SetMinimumWidth(iMinimumWidth);
1785 end;
1786 end;
1787
1788 for k, v in pairs(data) do
1789 options[#options + 1] = {k, v};
1790 end;
1791
1792 tableSort(options, function(a, b)
1793 return a[1] < b[1];
1794 end);
1795
1796 for k, v in pairs(options) do
1797 if (type(v[2]) == "table" and !v[2].isArgTable) then
1798 if (tableCount(v[2]) > 0) then
1799 self:AddMenuFromData(menuPanel:AddSubMenu(v[1]), v[2], Callback);
1800 end;
1801 elseif (type(v[2]) == "function") then
1802 menuPanel:AddOption(v[1], v[2]);
1803 elseif (Callback) then
1804 Callback(menuPanel, v[1], v[2]);
1805 end;
1806 end;
1807
1808 if (!bCreated) then return; end;
1809
1810 if (!bManualOpen) then
1811 if (#options > 0) then
1812 menuPanel:Open();
1813 else
1814 menuPanel:Remove();
1815 end;
1816 end;
1817
1818 return menuPanel;
1819 end;
1820
1821 -- A function to adjust the width of text.
1822 function Clockwork.kernel:AdjustMaximumWidth(font, text, width, addition, extra)
1823 local textString = tostring(self:Replace(text, "&", "U"));
1824 local textWidth = self:GetCachedTextSize(font, textString) + (extra or 0);
1825
1826 if (textWidth > width) then
1827 width = textWidth + (addition or 0);
1828 end;
1829
1830 return width;
1831 end;
1832
1833 --[[
1834 A function to add a center hint. If bNoSound is false then no
1835 sound will play, otherwise if it is a string then it will
1836 play that sound.
1837 --]]
1838 function Clockwork.kernel:AddCenterHint(text, delay, color, bNoSound, showDuplicated)
1839 local colorWhite = Clockwork.option:GetColor("white");
1840
1841 if (color) then
1842 if (type(color) == "string") then
1843 color = Clockwork.option:GetColor(color);
1844 end;
1845 else
1846 color = colorWhite;
1847 end;
1848
1849 if (!showDuplicated) then
1850 for k, v in pairs(self.CenterHints) do
1851 if (v.text == text) then
1852 return;
1853 end;
1854 end;
1855 end;
1856
1857 if (tableCount(self.CenterHints) == 10) then
1858 tableRemove(self.CenterHints, 10);
1859 end;
1860
1861 if (type(bNoSound) == "string") then
1862 surface.PlaySound(bNoSound);
1863 elseif (bNoSound == nil) then
1864 surface.PlaySound("hl1/fvox/blip.wav");
1865 end;
1866
1867 self.CenterHints[#self.CenterHints + 1] = {
1868 startTime = SysTime(),
1869 velocityX = -5,
1870 velocityY = 0,
1871 targetAlpha = 255,
1872 alphaSpeed = 64,
1873 color = color,
1874 delay = delay,
1875 alpha = 0,
1876 text = text,
1877 y = ScrH() * 0.6,
1878 x = ScrW() * 0.5
1879 };
1880 end;
1881
1882 local function UpdateCenterHint(index, hintInfo, iCount)
1883 local hintsFont = Clockwork.option:GetFont("hints_text");
1884 local fontWidth, fontHeight = Clockwork.kernel:GetCachedTextSize(
1885 hintsFont, hintInfo.text
1886 );
1887 local height = fontHeight;
1888 local width = fontWidth;
1889 local alpha = 255;
1890 local x = hintInfo.x;
1891 local y = hintInfo.y;
1892
1893 local idealY = (ScrH() * 0.4) + (height * (index - 1));
1894 local idealX = (ScrW() * 0.5) - (width * 0.5);
1895 local timeLeft = (hintInfo.startTime - (SysTime() - hintInfo.delay) + 2);
1896
1897 if (timeLeft < 0.7) then
1898 idealX = idealX - 50;
1899 alpha = 0;
1900 end;
1901
1902 if (timeLeft < 0.2) then
1903 idealX = idealX + width * 2;
1904 end;
1905
1906 local fSpeed = FrameTime() * 15;
1907 y = y + hintInfo.velocityY * fSpeed;
1908 x = x + hintInfo.velocityX * fSpeed;
1909 local distanceY = idealY - y;
1910 local distanceX = idealX - x;
1911 local distanceA = (alpha - hintInfo.alpha);
1912
1913 hintInfo.velocityY = hintInfo.velocityY + distanceY * fSpeed * 1;
1914 hintInfo.velocityX = hintInfo.velocityX + distanceX * fSpeed * 1;
1915
1916 if (mathAbs(distanceY) < 2 and mathAbs(hintInfo.velocityY) < 0.1) then
1917 hintInfo.velocityY = 0;
1918 end;
1919
1920 if (mathAbs(distanceX) < 2 and mathAbs(hintInfo.velocityX) < 0.1) then
1921 hintInfo.velocityX = 0;
1922 end;
1923
1924 hintInfo.velocityX = hintInfo.velocityX * (0.95 - FrameTime() * 8);
1925 hintInfo.velocityY = hintInfo.velocityY * (0.95 - FrameTime() * 8);
1926 hintInfo.alpha = hintInfo.alpha + distanceA * fSpeed * 0.1;
1927 hintInfo.x = x;
1928 hintInfo.y = y;
1929
1930 return (timeLeft < 0.1);
1931 end;
1932
1933 --[[
1934 A function to add a top hint. If bNoSound is false then no
1935 sound will play, otherwise if it is a string then it will
1936 play that sound.
1937 --]]
1938 function Clockwork.kernel:AddTopHint(text, delay, color, bNoSound, showDuplicated)
1939 local colorWhite = Clockwork.option:GetColor("white");
1940
1941 if (color) then
1942 if (type(color) == "string") then
1943 color = Clockwork.option:GetColor(color);
1944 end;
1945 else
1946 color = colorWhite;
1947 end;
1948
1949 if (!showDuplicated) then
1950 for k, v in pairs(self.Hints) do
1951 if (v.text == text) then
1952 return;
1953 end;
1954 end;
1955 end;
1956
1957 if (tableCount(self.Hints) == 10) then
1958 tableRemove(self.Hints, 10);
1959 end;
1960
1961 if (type(bNoSound) == "string") then
1962 surface.PlaySound(bNoSound);
1963 elseif (bNoSound == nil) then
1964 surface.PlaySound("hl1/fvox/blip.wav");
1965 end;
1966
1967 self.Hints[#self.Hints + 1] = {
1968 startTime = SysTime(),
1969 velocityX = -5,
1970 velocityY = 0,
1971 targetAlpha = 255,
1972 alphaSpeed = 64,
1973 color = color,
1974 delay = delay,
1975 alpha = 0,
1976 text = text,
1977 y = ScrH() * 0.2,
1978 x = ScrW()
1979 };
1980 end;
1981
1982 local function UpdateHint(index, hintInfo, iCount)
1983 local hintsFont = Clockwork.option:GetFont("hints_text");
1984 local fontWidth, fontHeight = Clockwork.kernel:GetCachedTextSize(
1985 hintsFont, hintInfo.text
1986 );
1987 local height = fontHeight;
1988 local width = fontWidth;
1989 local alpha = 255;
1990 local x = hintInfo.x;
1991 local y = hintInfo.y;
1992
1993 local idealY = 24 + (height * (index - 1));
1994 local idealX = ScrW() - width - 48;
1995 local timeLeft = (hintInfo.startTime - (SysTime() - hintInfo.delay) + 2);
1996
1997 if (timeLeft < 0.7) then
1998 idealX = idealX - 50;
1999 alpha = 0;
2000 end;
2001
2002 if (timeLeft < 0.2) then
2003 idealX = idealX + width * 2;
2004 end;
2005
2006 local fSpeed = FrameTime() * 15;
2007 y = y + hintInfo.velocityY * fSpeed;
2008 x = x + hintInfo.velocityX * fSpeed;
2009 local distanceY = idealY - y;
2010 local distanceX = idealX - x;
2011 local distanceA = (alpha - hintInfo.alpha);
2012
2013 hintInfo.velocityY = hintInfo.velocityY + distanceY * fSpeed * 1;
2014 hintInfo.velocityX = hintInfo.velocityX + distanceX * fSpeed * 1;
2015
2016 if (mathAbs(distanceY) < 2 and mathAbs(hintInfo.velocityY) < 0.1) then
2017 hintInfo.velocityY = 0;
2018 end;
2019
2020 if (mathAbs(distanceX) < 2 and mathAbs(hintInfo.velocityX) < 0.1) then
2021 hintInfo.velocityX = 0;
2022 end;
2023
2024 hintInfo.velocityX = hintInfo.velocityX * (0.95 - FrameTime() * 8);
2025 hintInfo.velocityY = hintInfo.velocityY * (0.95 - FrameTime() * 8);
2026 hintInfo.alpha = hintInfo.alpha + distanceA * fSpeed * 0.1;
2027 hintInfo.x = x;
2028 hintInfo.y = y;
2029
2030 return (timeLeft < 0.1);
2031 end;
2032
2033 -- A function to calculate the hints.
2034 function Clockwork.kernel:CalculateHints()
2035 for k, v in pairs(self.Hints) do
2036 if (UpdateHint(k, v, #self.Hints)) then
2037 tableRemove(self.Hints, k);
2038 end;
2039 end;
2040
2041 for k, v in pairs(self.CenterHints) do
2042 if (UpdateCenterHint(k, v, #self.CenterHints)) then
2043 tableRemove(self.CenterHints, k);
2044 end;
2045 end;
2046 end;
2047
2048 -- A utility function to draw text within an info block.
2049 local function Util_DrawText(info, text, color, bCentered, sFont)
2050 local realWidth = 0;
2051
2052 if (sFont) then Clockwork.kernel:OverrideMainFont(sFont); end;
2053
2054 if (!bCentered) then
2055 info.y, realWidth = Clockwork.kernel:DrawInfo(
2056 text, info.x - (info.width / 2), info.y, color, nil, true
2057 );
2058 else
2059 info.y, realWidth = Clockwork.kernel:DrawInfo(
2060 text, info.x, info.y, color
2061 );
2062 end;
2063
2064 if (realWidth > info.width) then
2065 info.width = realWidth + 16;
2066 end;
2067
2068 if (sFont) then
2069 Clockwork.kernel:OverrideMainFont(false);
2070 end;
2071 end;
2072
2073 -- A function to draw the date and time.
2074 function Clockwork.kernel:DrawDateTime()
2075 local backgroundColor = Clockwork.option:GetColor("background");
2076 local mainTextFont = Clockwork.option:GetFont("main_text");
2077 local colorWhite = Clockwork.option:GetColor("white");
2078 local colorInfo = Clockwork.option:GetColor("information");
2079 local scrW = ScrW();
2080 local scrH = ScrH();
2081 local info = {
2082 DrawText = Util_DrawText,
2083 width = mathMin(scrW * 0.5, 512),
2084 x = scrW / 2,
2085 y = scrH * 0.2
2086 };
2087
2088 info.originalX = info.x;
2089 info.originalY = info.y;
2090
2091 if (Clockwork.LastDateTimeInfo and Clockwork.LastDateTimeInfo.y > info.y) then
2092 local height = (Clockwork.LastDateTimeInfo.y - info.y) + 8;
2093 local width = Clockwork.LastDateTimeInfo.width + 16;
2094 local x = Clockwork.LastDateTimeInfo.x - (Clockwork.LastDateTimeInfo.width / 2) - 8;
2095 local y = Clockwork.LastDateTimeInfo.y - height - 8;
2096
2097 self:OverrideMainFont(Clockwork.option:GetFont("menu_text_tiny"));
2098 self:DrawInfo("CHARACTER AND ROLEPLAY INFO", x, y + 4, colorInfo, nil, true, function(x, y, width, height)
2099 return x, y - height;
2100 end);
2101
2102 SLICED_INFO_MENU_BG:Draw(x, y + 8, width, height, 8, backgroundColor);
2103 y = y + height + 16;
2104
2105 if (self:CanCreateInfoMenuPanel() and self:IsInfoMenuOpen()) then
2106 local menuPanelX = x;
2107 local menuPanelY = y;
2108
2109 self:DrawInfo("SELECT A QUICK MENU OPTION", x, y, colorInfo, nil, true, function(x, y, width, height)
2110 menuPanelY = menuPanelY + height + 8;
2111 return x, y;
2112 end);
2113
2114 self:CreateInfoMenuPanel(menuPanelX, menuPanelY, width);
2115
2116 SLICED_INFO_MENU_INSIDE:Draw( Clockwork.InfoMenuPanel.x - 4, Clockwork.InfoMenuPanel.y - 4, Clockwork.InfoMenuPanel:GetWide() + 8, Clockwork.InfoMenuPanel:GetTall() + 8, 8, backgroundColor);
2117
2118 --[[ Override the menu's width to fit nicely. --]]
2119 Clockwork.InfoMenuPanel:SetSize(width, Clockwork.InfoMenuPanel:GetTall());
2120 Clockwork.InfoMenuPanel:SetMinimumWidth(width);
2121
2122 if (!Clockwork.InfoMenuPanel.VisibilitySet) then
2123 Clockwork.InfoMenuPanel.VisibilitySet = true;
2124
2125 timer.Simple(FrameTime() * 2, function()
2126 if (IsValid(Clockwork.InfoMenuPanel)) then
2127 Clockwork.InfoMenuPanel:SetVisible(true);
2128 end;
2129 end);
2130 end;
2131 end;
2132
2133 self:OverrideMainFont(false);
2134 Clockwork.LastDateTimeInfo.height = height;
2135 end;
2136
2137 if (Clockwork.plugin:Call("PlayerCanSeeDateTime")) then
2138 local dateTimeFont = Clockwork.option:GetFont("date_time_text");
2139 local dateString = Clockwork.date:GetString();
2140 local timeString = Clockwork.time:GetString();
2141
2142 if (dateString and timeString) then
2143 local dayName = Clockwork.time:GetDayName();
2144 local text = stringUpper(dateString..". "..dayName..", "..timeString..".");
2145
2146 self:OverrideMainFont(dateTimeFont);
2147 info.y = self:DrawInfo(text, info.x, info.y, colorWhite, 255);
2148 self:OverrideMainFont(false);
2149 end;
2150 end;
2151
2152 self:DrawBars(info, "tab");
2153 Clockwork.PlayerInfoBox = self:DrawPlayerInfo(info);
2154 Clockwork.plugin:Call("PostDrawDateTimeBox", info);
2155 Clockwork.LastDateTimeInfo = info;
2156
2157 if (!Clockwork.plugin:Call("PlayerCanSeeLimbDamage")) then
2158 return;
2159 end;
2160
2161 local tipHeight = 0;
2162 local tipWidth = 0;
2163 local limbInfo = {};
2164 local height = 240;
2165 local width = 120;
2166 local texInfo = {
2167 shouldDisplay = true,
2168 textures = {
2169 [HITGROUP_RIGHTARM] = Clockwork.limb:GetTexture(HITGROUP_RIGHTARM),
2170 [HITGROUP_RIGHTLEG] = Clockwork.limb:GetTexture(HITGROUP_RIGHTLEG),
2171 [HITGROUP_LEFTARM] = Clockwork.limb:GetTexture(HITGROUP_LEFTARM),
2172 [HITGROUP_LEFTLEG] = Clockwork.limb:GetTexture(HITGROUP_LEFTLEG),
2173 [HITGROUP_STOMACH] = Clockwork.limb:GetTexture(HITGROUP_STOMACH),
2174 [HITGROUP_CHEST] = Clockwork.limb:GetTexture(HITGROUP_CHEST),
2175 [HITGROUP_HEAD] = Clockwork.limb:GetTexture(HITGROUP_HEAD),
2176 ["body"] = Clockwork.limb:GetTexture("body")
2177 },
2178 names = {
2179 [HITGROUP_RIGHTARM] = Clockwork.limb:GetName(HITGROUP_RIGHTARM),
2180 [HITGROUP_RIGHTLEG] = Clockwork.limb:GetName(HITGROUP_RIGHTLEG),
2181 [HITGROUP_LEFTARM] = Clockwork.limb:GetName(HITGROUP_LEFTARM),
2182 [HITGROUP_LEFTLEG] = Clockwork.limb:GetName(HITGROUP_LEFTLEG),
2183 [HITGROUP_STOMACH] = Clockwork.limb:GetName(HITGROUP_STOMACH),
2184 [HITGROUP_CHEST] = Clockwork.limb:GetName(HITGROUP_CHEST),
2185 [HITGROUP_HEAD] = Clockwork.limb:GetName(HITGROUP_HEAD),
2186 }
2187 };
2188 local x = info.x + (info.width / 2) + 32;
2189 local y = info.originalY + 8;
2190
2191 Clockwork.plugin:Call("GetPlayerLimbInfo", texInfo);
2192
2193 if (texInfo.shouldDisplay) then
2194 surface.SetDrawColor(255, 255, 255, 150);
2195 surface.SetMaterial(texInfo.textures["body"]);
2196 surface.DrawTexturedRect(x, y, width, height);
2197
2198 for k, v in pairs(Clockwork.limb.hitGroups) do
2199 local limbHealth = Clockwork.limb:GetHealth(k);
2200 local limbColor = Clockwork.limb:GetColor(limbHealth);
2201 local newIndex = #limbInfo + 1;
2202
2203 surface.SetDrawColor(limbColor.r, limbColor.g, limbColor.b, 150);
2204 surface.SetMaterial(texInfo.textures[k]);
2205 surface.DrawTexturedRect(x, y, width, height);
2206
2207 limbInfo[newIndex] = {
2208 color = limbColor,
2209 text = texInfo.names[k]..": "..limbHealth.."%"
2210 };
2211
2212 local textWidth, textHeight = self:GetCachedTextSize(mainTextFont, limbInfo[newIndex].text);
2213 tipHeight = tipHeight + textHeight + 4;
2214
2215 if (textWidth > tipWidth) then
2216 tipWidth = textWidth;
2217 end;
2218
2219 limbInfo[newIndex].textHeight = textHeight;
2220 end;
2221
2222 local mouseX = gui.MouseX();
2223 local mouseY = gui.MouseY();
2224
2225 if (mouseX >= x and mouseX <= x + width
2226 and mouseY >= y and mouseY <= y + height) then
2227 local tipX = mouseX + 16;
2228 local tipY = mouseY + 16;
2229
2230 self:DrawSimpleGradientBox(
2231 2, tipX - 8, tipY - 8, tipWidth + 16, tipHeight + 12, backgroundColor
2232 );
2233
2234 for k, v in pairs(limbInfo) do
2235 self:DrawInfo(v.text, tipX, tipY, v.color, 255, true);
2236
2237 if (k < #limbInfo) then
2238 tipY = tipY + v.textHeight + 4;
2239 else
2240 tipY = tipY + v.textHeight;
2241 end;
2242 end;
2243 end;
2244 end;
2245 end;
2246
2247 -- A function to draw the top hints.
2248 function Clockwork.kernel:DrawHints()
2249 if (Clockwork.plugin:Call("PlayerCanSeeHints") and #self.Hints > 0) then
2250 local hintsFont = Clockwork.option:GetFont("hints_text");
2251
2252 for k, v in pairs(self.Hints) do
2253 self:OverrideMainFont(hintsFont);
2254 self:DrawInfo(v.text, v.x, v.y, v.color, v.alpha, true);
2255 self:OverrideMainFont(false);
2256 end;
2257 end;
2258
2259 if (Clockwork.plugin:Call("PlayerCanSeeCenterHints") and #self.CenterHints > 0) then
2260 for k, v in pairs(self.CenterHints) do
2261 self:OverrideMainFont(hintsFont);
2262 self:DrawInfo(v.text, v.x, v.y, v.color, v.alpha, true);
2263 self:OverrideMainFont(false);
2264 end;
2265 end;
2266 end;
2267
2268 -- A function to draw the top bars.
2269 function Clockwork.kernel:DrawBars(info, class)
2270 if (Clockwork.plugin:Call("PlayerCanSeeBars", class)) then
2271 local barTextFont = Clockwork.option:GetFont("bar_text");
2272
2273 Clockwork.bars.width = info.width;
2274 Clockwork.bars.height = Clockwork.bars.height or 12;
2275 Clockwork.bars.padding = Clockwork.bars.padding or 14;
2276 Clockwork.bars.y = info.y;
2277
2278 if (class == "tab") then
2279 Clockwork.bars.x = info.x - (info.width / 2);
2280 else
2281 Clockwork.bars.x = info.x;
2282 end;
2283
2284 Clockwork.option:SetFont("bar_text", Clockwork.option:GetFont("auto_bar_text"));
2285 for k, v in pairs(Clockwork.bars.stored) do
2286 Clockwork.bars.y = self:DrawBar(Clockwork.bars.x, Clockwork.bars.y, Clockwork.bars.width, Clockwork.bars.height, v.color, v.text, v.value, v.maximum, v.flash, {uniqueID = v.uniqueID}) + (Clockwork.bars.padding + 2);
2287 end;
2288 Clockwork.option:SetFont("bar_text", barTextFont);
2289
2290 info.y = Clockwork.bars.y;
2291 end;
2292 end;
2293
2294 -- A function to get the ESP info.
2295 function Clockwork.kernel:GetESPInfo()
2296 return self.ESPInfo;
2297 end;
2298
2299 -- A function to draw the admin ESP.
2300 function Clockwork.kernel:DrawAdminESP()
2301 local colorWhite = Clockwork.option:GetColor("white");
2302 local curTime = UnPredictedCurTime();
2303
2304 if (!Clockwork.NextGetESPInfo or curTime >= Clockwork.NextGetESPInfo) then
2305 Clockwork.NextGetESPInfo = curTime + (CW_CONVAR_ESPTIME:GetInt() or 1);
2306 self.ESPInfo = {};
2307
2308 Clockwork.plugin:Call("GetAdminESPInfo", self.ESPInfo);
2309 end;
2310
2311 for k, v in pairs(self.ESPInfo) do
2312 local position = v.position:ToScreen();
2313 local text, color, height;
2314
2315 if (position) then
2316 if (type(v.text) == "string") then
2317 self:DrawSimpleText(v.text, position.x, position.y, v.color or colorWhite, 1, 1);
2318 else
2319 for k2, v2 in ipairs(v.text) do
2320 local barValue;
2321 local maximum = 100;
2322
2323 if (type(v2) == "string") then
2324 text = v2;
2325 color = v.color;
2326 else
2327 text = v2.text;
2328 color = v2.color;
2329
2330 local barNumbers = v2.bar;
2331
2332 if (type(barNumbers) == "table") then
2333 barValue = barNumbers.value;
2334 maximum = barNumbers.max;
2335 else
2336 barValue = barNumbers;
2337 end;
2338 end;
2339
2340 if (k2 > 1) then
2341 self:OverrideMainFont(Clockwork.option:GetFont("esp_text"));
2342 height = draw.GetFontHeight(Clockwork.option:GetFont("esp_text"));
2343 else
2344 self:OverrideMainFont(false);
2345 height = draw.GetFontHeight(Clockwork.option:GetFont("main_text"));
2346 end;
2347
2348 if (v2.icon) then
2349 local icon = "icon16/exclamation.png";
2350 local width = surface.GetTextSize(text);
2351
2352 if (type(v2.icon == "string") and v2.icon != "") then
2353 icon = v2.icon;
2354 end;
2355
2356 surface.SetDrawColor(255, 255, 255, 255);
2357 surface.SetMaterial(Clockwork.kernel:GetMaterial(icon));
2358 surface.DrawTexturedRect(position.x - (width * 0.40) - height, position.y - height * 0.5, height, height);
2359 end;
2360
2361 if (barValue and CW_CONVAR_ESPBARS:GetInt() == 1) then
2362 local barHeight = height * 0.80;
2363 local barColor = v2.barColor or Clockwork:GetValueColor(barValue);
2364 local grayColor = Color(150, 150, 150, 170);
2365 local progress = 100 * (barValue / maximum);
2366
2367 if progress < 0 then
2368 progress = 0;
2369 end;
2370
2371 draw.RoundedBox(6, position.x - 50, position.y - (barHeight * 0.45), 100, barHeight, grayColor);
2372 draw.RoundedBox(6, position.x - 50, position.y - (barHeight * 0.45), mathFloor(progress), barHeight, barColor);
2373 end;
2374
2375 if (type(text) == "string") then
2376 self:DrawSimpleText(text, position.x, position.y, color or colorWhite, 1, 1);
2377 end;
2378
2379 position.y = position.y + height;
2380 end;
2381 end;
2382 end;
2383 end;
2384 end;
2385
2386 -- A function to draw a bar with a value and a maximum.
2387 function Clockwork.kernel:DrawBar(x, y, width, height, color, text, value, maximum, flash, barInfo)
2388 local backgroundColor = Clockwork.option:GetColor("background");
2389 local foregroundColor = Clockwork.option:GetColor("foreground");
2390 local progressWidth = mathClamp(((width - 4) / maximum) * value, 0, width - 4);
2391 local colorWhite = Clockwork.option:GetColor("white");
2392 local newBarInfo = {
2393 progressWidth = progressWidth,
2394 drawBackground = true,
2395 drawProgress = true,
2396 cornerSize = 2,
2397 maximum = maximum,
2398 height = height,
2399 width = width,
2400 color = color,
2401 value = value,
2402 flash = flash,
2403 text = text,
2404 x = x,
2405 y = y
2406 };
2407
2408 if (barInfo) then
2409 for k, v in pairs(newBarInfo) do
2410 if (!barInfo[k]) then
2411 barInfo[k] = v;
2412 end;
2413 end;
2414 else
2415 barInfo = newBarInfo;
2416 end;
2417
2418 if (!Clockwork.plugin:Call("PreDrawBar", barInfo)) then
2419 if (barInfo.drawBackground) then
2420 SMALL_BAR_BG:Draw(barInfo.x, barInfo.y, barInfo.width, barInfo.height, barInfo.cornerSize, backgroundColor, 50);
2421 end;
2422
2423 if (barInfo.drawProgress) then
2424 render.SetScissorRect(barInfo.x, barInfo.y, barInfo.x + barInfo.progressWidth, barInfo.y + barInfo.height, true);
2425 SMALL_BAR_FG:Draw(barInfo.x + 2, barInfo.y + 2, barInfo.width - 4, barInfo.height - 4, 3, barInfo.color, 150);
2426 render.SetScissorRect(barInfo.x, barInfo.y, barInfo.x + barInfo.progressWidth, barInfo.height, false);
2427 end;
2428
2429 if (barInfo.flash) then
2430 local alpha = mathClamp(mathAbs(mathSin(UnPredictedCurTime()) * 50), 0, 50);
2431
2432 if (alpha > 0) then
2433 draw.RoundedBox(0, barInfo.x + 2, barInfo.y + 2, barInfo.width - 4, barInfo.height - 4,
2434 Color(colorWhite.r, colorWhite.g, colorWhite.b, alpha));
2435 end;
2436 end;
2437 end;
2438
2439 if (!Clockwork.plugin:Call("PostDrawBar", barInfo)) then
2440 if (barInfo.text and barInfo.text != "") then
2441 self:OverrideMainFont(Clockwork.option:GetFont("bar_text"));
2442 self:DrawSimpleText(
2443 barInfo.text, barInfo.x + (barInfo.width / 2), barInfo.y + (barInfo.height / 2),
2444 Color(colorWhite.r, colorWhite.g, colorWhite.b, alpha), 1, 1
2445 );
2446 self:OverrideMainFont(false);
2447 end;
2448 end;
2449
2450 return barInfo.y;
2451 end;
2452
2453 -- A function to set the recognise menu.
2454 function Clockwork.kernel:SetRecogniseMenu(menuPanel)
2455 Clockwork.RecogniseMenu = menuPanel;
2456 self:SetTitledMenu(menuPanel, "SELECT WHO CAN RECOGNISE YOU");
2457 end;
2458
2459 -- A function to get the recognise menu.
2460 function Clockwork.kernel:GetRecogniseMenu(menuPanel)
2461 return Clockwork.RecogniseMenu;
2462 end;
2463
2464 -- A function to override the main font.
2465 function Clockwork.kernel:OverrideMainFont(font)
2466 if (font) then
2467 if (!Clockwork.PreviousMainFont) then
2468 Clockwork.PreviousMainFont = Clockwork.option:GetFont("main_text");
2469 end;
2470
2471 Clockwork.option:SetFont("main_text", font);
2472 elseif (Clockwork.PreviousMainFont) then
2473 Clockwork.option:SetFont("main_text", Clockwork.PreviousMainFont)
2474 end;
2475 end;
2476
2477 -- A function to get the screen's center.
2478 function Clockwork.kernel:GetScreenCenter()
2479 return ScrW() / 2, (ScrH() / 2) + 32;
2480 end;
2481
2482 -- A function to draw some simple text.
2483 function Clockwork.kernel:DrawSimpleText(text, x, y, color, alignX, alignY, shadowless, shadowDepth)
2484 local mainTextFont = Clockwork.option:GetFont("main_text");
2485 local realX = mathRound(x);
2486 local realY = mathRound(y);
2487
2488 if (!shadowless) then
2489 local outlineColor = Color(25, 25, 25, mathMin(225, color.a));
2490
2491 for i = 1, (shadowDepth or 1) do
2492 draw.SimpleText(text, mainTextFont, realX + -i, realY + -i, outlineColor, alignX, alignY);
2493 draw.SimpleText(text, mainTextFont, realX + -i, realY + i, outlineColor, alignX, alignY);
2494 draw.SimpleText(text, mainTextFont, realX + i, realY + -i, outlineColor, alignX, alignY);
2495 draw.SimpleText(text, mainTextFont, realX + i, realY + i, outlineColor, alignX, alignY);
2496 end;
2497 end;
2498
2499 draw.SimpleText(text, mainTextFont, realX, realY, color, alignX, alignY);
2500 local width, height = self:GetCachedTextSize(mainTextFont, text);
2501
2502 return realY + height + 2, width;
2503 end;
2504
2505 -- A function to get the black fade alpha.
2506 function Clockwork.kernel:GetBlackFadeAlpha()
2507 return Clockwork.BlackFadeIn or Clockwork.BlackFadeOut or 0;
2508 end;
2509
2510 -- A function to get whether the screen is faded black.
2511 function Clockwork.kernel:IsScreenFadedBlack()
2512 return (Clockwork.BlackFadeIn == 255);
2513 end;
2514
2515 --[[
2516 A function to print colored text to the console.
2517 Sure, it's hacky, but Garry is being a douche.
2518 --]]
2519 function Clockwork.kernel:PrintColoredText(...)
2520 local currentColor = nil;
2521 local colorWhite = Clockwork.option:GetColor("white");
2522 local text = {};
2523
2524 for k, v in pairs({...}) do
2525 if (type(v) == "Player") then
2526 text[#text + 1] = cwTeam.GetColor(v:Team());
2527 text[#text + 1] = v:Name();
2528 elseif (type(v) == "table") then
2529 currentColor = v;
2530 elseif (currentColor) then
2531 text[#text + 1] = currentColor;
2532 text[#text + 1] = v;
2533 currentColor = nil;
2534 else
2535 text[#text + 1] = colorWhite;
2536 text[#text + 1] = v;
2537 end;
2538 end;
2539
2540 chat.ClockworkAddText(unpack(text));
2541 end;
2542
2543 -- A function to get whether a custom crosshair is used.
2544 function Clockwork.kernel:UsingCustomCrosshair()
2545 return Clockwork.CustomCrosshair;
2546 end;
2547
2548 -- A function to get a cached text size.
2549 function Clockwork.kernel:GetCachedTextSize(font, text)
2550 if (!Clockwork.CachedTextSizes) then
2551 Clockwork.CachedTextSizes = {};
2552 end;
2553
2554 if (!Clockwork.CachedTextSizes[font]) then
2555 Clockwork.CachedTextSizes[font] = {};
2556 end;
2557
2558 if (!Clockwork.CachedTextSizes[font][text]) then
2559 surface.SetFont(font);
2560
2561 Clockwork.CachedTextSizes[font][text] = { surface.GetTextSize(text) };
2562 end;
2563
2564 return Clockwork.CachedTextSizes[font][text][1], Clockwork.CachedTextSizes[font][text][2];
2565 end;
2566
2567 -- A function to draw scaled information at a position.
2568 function Clockwork.kernel:DrawInfoScaled(scale, text, x, y, color, alpha, bAlignLeft, Callback, shadowDepth)
2569 local newFont = Clockwork.fonts:GetMultiplied("cwMainText", scale);
2570 local returnY = 0;
2571
2572 self:OverrideMainFont(newFont);
2573
2574 returnY = self:DrawInfo(text, x, y, color, alpha, bAlignLeft, Callback, shadowDepth);
2575
2576 self:OverrideMainFont(false);
2577
2578 return returnY;
2579 end;
2580
2581 -- A function to draw information at a position.
2582 function Clockwork.kernel:DrawInfo(text, x, y, color, alpha, bAlignLeft, Callback, shadowDepth)
2583 local mainTextFont = Clockwork.option:GetFont("main_text");
2584 local width, height = self:GetCachedTextSize(mainTextFont, text);
2585
2586 if (width and height) then
2587 if (!bAlignLeft) then
2588 x = x - (width / 2);
2589 end;
2590
2591 if (Callback) then
2592 x, y = Callback(x, y, width, height);
2593 end;
2594
2595 return self:DrawSimpleText(text, x, y, Color(color.r, color.g, color.b, alpha or color.a), nil, nil, nil, shadowDepth);
2596 end;
2597 end;
2598
2599 -- A function to get the player info box.
2600 function Clockwork.kernel:GetPlayerInfoBox()
2601 return Clockwork.PlayerInfoBox;
2602 end;
2603
2604 -- A function to draw the local player's information.
2605 function Clockwork.kernel:DrawPlayerInfo(info)
2606 if (!Clockwork.plugin:Call("PlayerCanSeePlayerInfo")) then
2607 return;
2608 end;
2609
2610 local foregroundColor = Clockwork.option:GetColor("foreground");
2611 local subInformation = Clockwork.PlayerInfoText.subText;
2612 local information = Clockwork.PlayerInfoText.text;
2613 local colorWhite = Clockwork.option:GetColor("white");
2614 local textWidth, textHeight = self:GetCachedTextSize(
2615 Clockwork.option:GetFont("player_info_text"), "U"
2616 );
2617 local width = Clockwork.PlayerInfoText.width;
2618
2619 if (width < info.width) then
2620 width = info.width;
2621 elseif (width > width) then
2622 info.width = width;
2623 end;
2624
2625 if (#information == 0 and #subInformation == 0) then
2626 return;
2627 end;
2628
2629 local height = (textHeight * #information) + ((textHeight + 12) * #subInformation);
2630 local scrW = ScrW();
2631 local scrH = ScrH();
2632
2633 if (#information > 0) then
2634 height = height + 8;
2635 end;
2636
2637 local y = info.y + 8;
2638 local x = info.x - (width / 2);
2639
2640 local boxInfo = {
2641 subInformation = subInformation,
2642 drawBackground = true,
2643 information = information,
2644 textHeight = textHeight,
2645 cornerSize = 2,
2646 textWidth = textWidth,
2647 height = height,
2648 width = width,
2649 x = x,
2650 y = y
2651 };
2652
2653 if (!Clockwork.plugin:Call("PreDrawPlayerInfo", boxInfo, information, subInformation)) then
2654 self:OverrideMainFont(Clockwork.option:GetFont("player_info_text"));
2655
2656 for k, v in pairs(subInformation) do
2657 x, y = self:DrawPlayerInfoSubBox(v.text, x, y, width, boxInfo);
2658 end;
2659
2660 if (#information > 0 and boxInfo.drawBackground) then
2661 SLICED_PLAYER_INFO:Draw(x, y, width, height - ((textHeight + 12) * #subInformation), boxInfo.cornerSize);
2662 end;
2663
2664 if (#information > 0) then
2665 x = x + 8
2666 y = y + 4;
2667 end;
2668
2669 for k, v in pairs(information) do
2670 self:DrawInfo(v.text, x, y - 1, colorWhite, 255, true);
2671 y = y + textHeight;
2672 end;
2673
2674 self:OverrideMainFont(false);
2675 end;
2676
2677 Clockwork.plugin:Call("PostDrawPlayerInfo", boxInfo, information, subInformation);
2678 info.y = info.y + boxInfo.height + 12;
2679
2680 return boxInfo;
2681 end;
2682
2683 -- A function to get whether the info menu panel can be created.
2684 function Clockwork.kernel:CanCreateInfoMenuPanel()
2685 return (tableCount(Clockwork.quickmenu.stored) > 0 or tableCount(Clockwork.quickmenu.categories) > 0);
2686 end;
2687
2688 -- A function to create the info menu panel.
2689 function Clockwork.kernel:CreateInfoMenuPanel(x, y, iMinimumWidth)
2690 if (IsValid(Clockwork.InfoMenuPanel)) then return; end;
2691
2692 local options = {};
2693
2694 for k, v in pairs(Clockwork.quickmenu.categories) do
2695 options[k] = {};
2696
2697 for k2, v2 in pairs(v) do
2698 local info = v2.GetInfo();
2699
2700 if (type(info) == "table") then
2701 options[k][k2] = info;
2702 options[k][k2].isArgTable = true;
2703 end;
2704 end;
2705 end;
2706
2707 for k, v in pairs(Clockwork.quickmenu.stored) do
2708 local info = v.GetInfo();
2709
2710 if (type(info) == "table") then
2711 options[k] = info;
2712 options[k].isArgTable = true;
2713 end;
2714 end;
2715
2716 Clockwork.InfoMenuPanel = self:AddMenuFromData(nil, options, function(menuPanel, option, arguments)
2717 if (arguments.name) then
2718 option = arguments.name;
2719 end;
2720
2721 if (arguments.options) then
2722 local subMenu = menuPanel:AddSubMenu(option);
2723
2724 for k, v in pairs(arguments.options) do
2725 local name = v;
2726
2727 if (type(v) == "table") then
2728 name = v[1];
2729 end;
2730
2731 subMenu:AddOption(name, function()
2732 if (arguments.Callback) then
2733 if (type(v) == "table") then
2734 arguments.Callback(v[2]);
2735 else
2736 arguments.Callback(v);
2737 end;
2738 end;
2739
2740 self:RemoveActiveToolTip();
2741 self:CloseActiveDermaMenus();
2742 end);
2743 end;
2744
2745 if (IsValid(subMenu)) then
2746 if (arguments.toolTip) then
2747 subMenu:SetToolTip(arguments.toolTip);
2748 end;
2749 end;
2750 else
2751 menuPanel:AddOption(option, function()
2752 if (arguments.Callback) then
2753 arguments.Callback();
2754 end;
2755
2756 self:RemoveActiveToolTip();
2757 self:CloseActiveDermaMenus();
2758 end);
2759
2760 menuPanel.Items = menuPanel:GetChildren();
2761 local panel = menuPanel.Items[#menuPanel.Items];
2762
2763 if (IsValid(panel) and arguments.toolTip) then
2764 panel:SetToolTip(arguments.toolTip);
2765 end;
2766 end;
2767 end, iMinimumWidth);
2768
2769 if (IsValid(Clockwork.InfoMenuPanel)) then
2770 Clockwork.InfoMenuPanel:SetVisible(false);
2771 Clockwork.InfoMenuPanel:SetSize(iMinimumWidth, Clockwork.InfoMenuPanel:GetTall());
2772 Clockwork.InfoMenuPanel:SetPos(x, y);
2773 end;
2774 end;
2775
2776 -- A function to get the ragdoll eye angles.
2777 function Clockwork.kernel:GetRagdollEyeAngles()
2778 if (!Clockwork.RagdollEyeAngles) then
2779 Clockwork.RagdollEyeAngles = Angle(0, 0, 0);
2780 end;
2781
2782 return Clockwork.RagdollEyeAngles;
2783 end;
2784
2785 -- A function to draw a gradient.
2786 function Clockwork.kernel:DrawGradient(gradientType, x, y, width, height, color)
2787 if (!Clockwork.Gradients[gradientType]) then
2788 return;
2789 end;
2790
2791 surface.SetDrawColor(color.r, color.g, color.b, color.a);
2792 surface.SetTexture(Clockwork.Gradients[gradientType]);
2793 surface.DrawTexturedRect(x, y, width, height);
2794 end;
2795
2796 -- A function to draw a simple gradient box.
2797 function Clockwork.kernel:DrawSimpleGradientBox(cornerSize, x, y, width, height, color, maxAlpha)
2798 local gradientAlpha = mathMin(color.a, maxAlpha or 100);
2799
2800 draw.RoundedBox(cornerSize, x, y, width, height, Color(color.r, color.g, color.b, color.a * 0.75));
2801
2802 if (x + cornerSize < x + width and y + cornerSize < y + height) then
2803 surface.SetDrawColor(gradientAlpha, gradientAlpha, gradientAlpha, gradientAlpha);
2804 surface.SetMaterial(self:GetGradientTexture());
2805 surface.DrawTexturedRect(x + cornerSize, y + cornerSize, width - (cornerSize * 2), height - (cornerSize * 2));
2806 end;
2807 end;
2808
2809 -- A function to draw a textured gradient.
2810 function Clockwork.kernel:DrawTexturedGradientBox(cornerSize, x, y, width, height, color, maxAlpha)
2811 local gradientAlpha = mathMin(color.a, maxAlpha or 100);
2812
2813 draw.RoundedBox(cornerSize, x, y, width, height, Color(color.r, color.g, color.b, color.a * 0.75));
2814
2815 if (x + cornerSize < x + width and y + cornerSize < y + height) then
2816 surface.SetDrawColor(gradientAlpha, gradientAlpha, gradientAlpha, gradientAlpha);
2817 surface.SetMaterial(self:GetGradientTexture());
2818 surface.DrawTexturedRect(x + cornerSize, y + cornerSize, width - (cornerSize * 2), height - (cornerSize * 2));
2819 end;
2820 end;
2821
2822 -- A function to draw a player information sub box.
2823 function Clockwork.kernel:DrawPlayerInfoSubBox(text, x, y, width, boxInfo)
2824 local foregroundColor = Clockwork.option:GetColor("foreground");
2825 local colorInfo = Clockwork.option:GetColor("information");
2826 local boxHeight = boxInfo.textHeight + 8;
2827
2828 if (boxInfo.drawBackground) then
2829 SLICED_PLAYER_INFO:Draw(x, y, width, boxHeight, 4, foregroundColor, 50);
2830 end;
2831
2832 self:DrawInfo(text, x + 8, y + (boxHeight / 2), colorInfo, 255, true,
2833 function(x, y, width, height)
2834 return x, y - (height / 2);
2835 end
2836 );
2837
2838 return x, y + boxHeight + 4;
2839 end;
2840
2841 -- A function to handle an item's spawn icon click.
2842 function Clockwork.kernel:HandleItemSpawnIconClick(itemTable, spawnIcon, Callback)
2843 local customFunctions = itemTable("customFunctions");
2844 local itemFunctions = {};
2845 local destroyName = Clockwork.option:GetKey("name_destroy");
2846 local dropName = Clockwork.option:GetKey("name_drop");
2847 local useName = Clockwork.option:GetKey("name_use");
2848
2849 if (itemTable.OnUse) then
2850 itemFunctions[#itemFunctions + 1] = itemTable("useText", useName);
2851 end;
2852
2853 if (itemTable.OnDrop) then
2854 itemFunctions[#itemFunctions + 1] = itemTable("dropText", dropName);
2855 end;
2856
2857 if (itemTable.OnDestroy) then
2858 itemFunctions[#itemFunctions + 1] = itemTable("destroyText", destroyName);
2859 end;
2860
2861 if (customFunctions) then
2862 for k, v in pairs(customFunctions) do
2863 itemFunctions[#itemFunctions + 1] = v;
2864 end;
2865 end;
2866
2867 if (itemTable.GetOptions) then
2868 local options = itemTable:GetOptions(nil, nil);
2869 for k, v in pairs(options) do
2870 itemFunctions[#itemFunctions + 1] = {title = k, name = v};
2871 end
2872 end
2873
2874 if (itemTable.OnEditFunctions) then
2875 itemTable:OnEditFunctions(itemFunctions);
2876 end;
2877
2878 Clockwork.plugin:Call("PlayerAdjustItemFunctions", itemTable, itemFunctions);
2879 self:ValidateTableKeys(itemFunctions);
2880
2881 tableSort(itemFunctions, function(a, b) return ((type(a) == "table" and a.title) or a) < ((type(b) == "table" and b.title) or b); end);
2882 if (#itemFunctions == 0 and !Callback) then return; end;
2883
2884 local options = {};
2885
2886 if (itemTable.GetEntityMenuOptions) then
2887 itemTable:GetEntityMenuOptions(nil, options);
2888 end;
2889
2890 local itemMenu = self:AddMenuFromData(nil, options, function(menuPanel, option, arguments)
2891 menuPanel:AddOption(option, function()
2892 if (type(arguments) == "table" and arguments.isArgTable) then
2893 if (arguments.Callback) then
2894 arguments.Callback();
2895 end;
2896 elseif (arguments == "function") then
2897 arguments();
2898 end;
2899
2900 timer.Simple(FrameTime(), function()
2901 self:RemoveActiveToolTip();
2902 end);
2903 end);
2904
2905 menuPanel.Items = menuPanel:GetChildren();
2906 local panel = menuPanel.Items[#menuPanel.Items];
2907
2908 if (IsValid(panel)) then
2909 if (type(arguments) == "table") then
2910 if (arguments.toolTip) then
2911 self:CreateMarkupToolTip(panel);
2912 panel:SetMarkupToolTip(arguments.toolTip);
2913 end;
2914 end;
2915 end;
2916 end, nil, true);
2917
2918 if (Callback) then Callback(itemMenu); end;
2919
2920 itemMenu:SetMinimumWidth(100);
2921 Clockwork.plugin:Call("PlayerAdjustItemMenu", itemTable, itemMenu, itemFunctions);
2922
2923 for k, v in pairs(itemFunctions) do
2924 local useText = itemTable("useText", "Use");
2925 local dropText = itemTable("dropText", "Drop");
2926 local destroyText = itemTable("destroyText", "Destroy");
2927
2928 if ((!useText and v == "Use") or (useText and v == useText)) then
2929 itemMenu:AddOption(v, function()
2930 if (itemTable) then
2931 if (itemTable.OnHandleUse) then
2932 itemTable:OnHandleUse(function()
2933 self:RunCommand(
2934 "InvAction", "use", itemTable("uniqueID"), itemTable("itemID")
2935 );
2936 end);
2937 else
2938 self:RunCommand(
2939 "InvAction", "use", itemTable("uniqueID"), itemTable("itemID")
2940 );
2941 end;
2942 end;
2943 end);
2944 elseif ((!dropText and v == "Drop") or (dropText and v == dropText)) then
2945 itemMenu:AddOption(v, function()
2946 if (itemTable) then
2947 self:RunCommand(
2948 "InvAction", "drop", itemTable("uniqueID"), itemTable("itemID")
2949 );
2950 end;
2951 end);
2952 elseif ((!destroyText and v == "Destroy") or (destroyText and v == destroyText)) then
2953 local subMenu = itemMenu:AddSubMenu(v);
2954
2955 subMenu:AddOption("Yes", function()
2956 if (itemTable) then
2957 self:RunCommand(
2958 "InvAction", "destroy", itemTable("uniqueID"), itemTable("itemID")
2959 );
2960 end;
2961 end);
2962
2963 subMenu:AddOption("No", function() end);
2964 elseif (type(v) == "table") then
2965 itemMenu:AddOption(v.title, function()
2966 local defaultAction = true;
2967
2968 if (itemTable.HandleOptions) then
2969 local transmit, data = itemTable:HandleOptions(v.name);
2970
2971 if (transmit) then
2972 Clockwork.datastream:Start("MenuOption", {option = v.name, data = data, item = itemTable("itemID")});
2973 defaultAction = false;
2974 end;
2975 end;
2976
2977 if (defaultAction) then
2978 self:RunCommand(
2979 "InvAction", v.name, itemTable("uniqueID"), itemTable("itemID")
2980 );
2981 end;
2982 end);
2983 else
2984 if (itemTable.OnCustomFunction) then
2985 itemTable:OnCustomFunction(v);
2986 end;
2987
2988 itemMenu:AddOption(v, function()
2989 if (itemTable) then
2990 self:RunCommand(
2991 "InvAction", v, itemTable("uniqueID"), itemTable("itemID")
2992 );
2993 end;
2994 end);
2995 end;
2996 end;
2997
2998 itemMenu:Open();
2999 end;
3000
3001 -- A function to handle an item's spawn icon right click.
3002 function Clockwork.kernel:HandleItemSpawnIconRightClick(itemTable, spawnIcon)
3003 if (itemTable.OnHandleRightClick) then
3004 local functionName = itemTable:OnHandleRightClick();
3005
3006 if (functionName and functionName != "Use") then
3007 local customFunctions = itemTable("customFunctions");
3008
3009 if (customFunctions and tableHasValue(customFunctions, functionName)) then
3010 if (itemTable.OnCustomFunction) then
3011 itemTable:OnCustomFunction(v);
3012 end;
3013 end;
3014
3015 self:RunCommand(
3016 "InvAction", stringLower(functionName), itemTable("uniqueID"), itemTable("itemID")
3017 );
3018 return;
3019 end;
3020 end;
3021
3022 if (itemTable.OnUse) then
3023 if (itemTable.OnHandleUse) then
3024 itemTable:OnHandleUse(function()
3025 self:RunCommand("InvAction", "use", itemTable("uniqueID"), itemTable("itemID"));
3026 end);
3027 else
3028 self:RunCommand("InvAction", "use", itemTable("uniqueID"), itemTable("itemID"));
3029 end;
3030 end;
3031 end;
3032
3033 -- A function to set a panel's perform layout callback.
3034 function Clockwork.kernel:SetOnLayoutCallback(target, Callback)
3035 if (target.PerformLayout) then
3036 target.OldPerformLayout = target.PerformLayout;
3037
3038 -- Called when the panel's layout is performed.
3039 function target.PerformLayout()
3040 target:OldPerformLayout(); Callback(target);
3041 end;
3042 end;
3043 end;
3044
3045 -- A function to set the active titled DMenu.
3046 function Clockwork.kernel:SetTitledMenu(menuPanel, title)
3047 Clockwork.TitledMenu = {
3048 menuPanel = menuPanel,
3049 title = title
3050 };
3051 end;
3052
3053 -- A function to add a markup line.
3054 function Clockwork.kernel:AddMarkupLine(markupText, text, color)
3055 if (markupText != "") then
3056 markupText = markupText.."\n";
3057 end;
3058
3059 return markupText..self:MarkupTextWithColor(text, color);
3060 end;
3061
3062 -- A function to draw a markup tool tip.
3063 function Clockwork.kernel:DrawMarkupToolTip(markupObject, x, y, alpha)
3064 local height = markupObject:GetHeight();
3065 local width = markupObject:GetWidth();
3066
3067 if (x - (width / 2) > 0) then
3068 x = x - (width / 2);
3069 end;
3070
3071 if (x + width > ScrW()) then
3072 x = x - width - 8;
3073 end;
3074
3075 if (y + (height + 8) > ScrH()) then
3076 y = y - height - 8;
3077 end;
3078
3079 self:DrawSimpleGradientBox(2, x - 8, y - 8, width + 16, height + 16, Color(50, 50, 50, alpha));
3080 markupObject:Draw(x, y, nil, nil, alpha);
3081 end;
3082
3083 -- A function to override a markup object's draw function.
3084 function Clockwork.kernel:OverrideMarkupDraw(markupObject, sCustomFont)
3085 function markupObject:Draw(xOffset, yOffset, hAlign, vAlign, alphaOverride)
3086 for k, v in pairs(self.blocks) do
3087 if (!v.colour) then
3088 debug.Trace();
3089 return;
3090 end;
3091
3092 local alpha = v.colour.a or 255;
3093 local y = yOffset + (v.height - v.thisY) + v.offset.y;
3094 local x = xOffset;
3095
3096 if (hAlign == TEXT_ALIGN_CENTER) then
3097 x = x - (self.totalWidth / 2);
3098 elseif (hAlign == TEXT_ALIGN_RIGHT) then
3099 x = x - self.totalWidth;
3100 end;
3101
3102 x = x + v.offset.x;
3103
3104 if (hAlign == TEXT_ALIGN_CENTER) then
3105 y = y - (self.totalHeight / 2);
3106 elseif (hAlign == TEXT_ALIGN_BOTTOM) then
3107 y = y - self.totalHeight;
3108 end;
3109
3110 if (alphaOverride) then
3111 alpha = alphaOverride;
3112 end;
3113
3114 Clockwork.kernel:OverrideMainFont(sCustomFont or v.font);
3115 Clockwork.kernel:DrawSimpleText(v.text, x, y, Color(v.colour.r, v.colour.g, v.colour.b, alpha));
3116 Clockwork.kernel:OverrideMainFont(false);
3117 end;
3118 end;
3119 end;
3120
3121 -- A function to get the active markup tool tip.
3122 function Clockwork.kernel:GetActiveMarkupToolTip()
3123 return Clockwork.MarkupToolTip;
3124 end;
3125
3126 -- A function to get markup from a color.
3127 function Clockwork.kernel:ColorToMarkup(color)
3128 return "<color="..mathCeil(color.r)..","..mathCeil(color.g)..","..mathCeil(color.b)..">";
3129 end;
3130
3131 -- A function to markup text with a color.
3132 function Clockwork.kernel:MarkupTextWithColor(text, color, scale)
3133 local fontName = Clockwork.fonts:GetMultiplied("cwTooltip", scale or 1);
3134 local finalText = text;
3135
3136 if (color) then
3137 finalText = self:ColorToMarkup(color)..text.."</color>";
3138 end;
3139
3140 finalText = "<font="..fontName..">"..finalText.."</font>";
3141
3142 return finalText;
3143 end;
3144
3145 -- A function to create a markup tool tip.
3146 function Clockwork.kernel:CreateMarkupToolTip(panel)
3147 panel.OldCursorExited = panel.OnCursorExited;
3148 panel.OldCursorEntered = panel.OnCursorEntered;
3149
3150 -- Called when the cursor enters the panel.
3151 function panel.OnCursorEntered(panel, ...)
3152 if (panel.OldCursorEntered) then
3153 panel:OldCursorEntered(...);
3154 end;
3155
3156 Clockwork.MarkupToolTip = panel;
3157 end;
3158
3159 -- Called when the cursor exits the panel.
3160 function panel.OnCursorExited(panel, ...)
3161 if (panel.OldCursorExited) then
3162 panel:OldCursorExited(...);
3163 end;
3164
3165 if (Clockwork.MarkupToolTip == panel) then
3166 Clockwork.MarkupToolTip = nil;
3167 end;
3168 end;
3169
3170 -- A function to set the panel's markup tool tip.
3171 function panel.SetMarkupToolTip(panel, text)
3172 if (!panel.MarkupToolTip or panel.MarkupToolTip.text != text) then
3173 panel.MarkupToolTip = {
3174 object = markup.Parse(text, ScrW() * 0.25),
3175 text = text
3176 };
3177
3178 self:OverrideMarkupDraw(panel.MarkupToolTip.object);
3179 end;
3180 end;
3181
3182 -- A function to get the panel's markup tool tip.
3183 function panel.GetMarkupToolTip(panel)
3184 return panel.MarkupToolTip;
3185 end;
3186
3187 -- A function to set the panel's tool tip.
3188 function panel.SetToolTip(panel, toolTip)
3189 panel:SetMarkupToolTip(toolTip);
3190 end;
3191
3192 return panel;
3193 end;
3194
3195 -- A function to create a custom category panel.
3196 function Clockwork.kernel:CreateCustomCategoryPanel(categoryName, parent)
3197 if (!parent.CategoryList) then
3198 parent.CategoryList = {};
3199 end;
3200
3201 local collapsibleCategory = vgui.Create("DCollapsibleCategory", parent);
3202 collapsibleCategory:SetExpanded(true);
3203 collapsibleCategory:SetPadding(2);
3204 collapsibleCategory:SetLabel(categoryName);
3205 parent.CategoryList[#parent.CategoryList + 1] = collapsibleCategory;
3206
3207 return collapsibleCategory;
3208 end;
3209
3210 -- A function to draw the armor bar.
3211 function Clockwork.kernel:DrawArmorBar()
3212 local armor = mathClamp(Clockwork.Client:Armor(), 0, Clockwork.Client:GetMaxArmor());
3213
3214 if (!self.armor) then
3215 self.armor = armor;
3216 else
3217 self.armor = mathApproach(self.armor, armor, 1);
3218 end;
3219
3220 if (armor > 0) then
3221 Clockwork.bars:Add("ARMOR", Color(139, 174, 179, 255), "", self.armor, Clockwork.Client:GetMaxArmor(), self.health < 10, 1);
3222 end;
3223 end;
3224
3225 -- A function to draw the health bar.
3226 function Clockwork.kernel:DrawHealthBar()
3227 local health = mathClamp(Clockwork.Client:Health(), 0, Clockwork.Client:GetMaxHealth());
3228
3229 if (!self.armor) then
3230 self.health = health;
3231 else
3232 self.health = mathApproach(self.health, health, 1);
3233 end;
3234
3235 if (health > 0) then
3236 Clockwork.bars:Add("HEALTH", Color(179, 46, 49, 255), "", self.health, Clockwork.Client:GetMaxHealth(), self.health < 10, 2);
3237 end;
3238 end;
3239
3240 -- A function to remove the active tool tip.
3241 function Clockwork.kernel:RemoveActiveToolTip()
3242 ChangeTooltip();
3243 end;
3244
3245 -- A function to close active Derma menus.
3246 function Clockwork.kernel:CloseActiveDermaMenus()
3247 CloseDermaMenus();
3248 end;
3249
3250 -- A function to register a background blur.
3251 function Clockwork.kernel:RegisterBackgroundBlur(panel, fCreateTime)
3252 Clockwork.BackgroundBlurs[panel] = fCreateTime or SysTime();
3253 end;
3254
3255 -- A function to remove a background blur.
3256 function Clockwork.kernel:RemoveBackgroundBlur(panel)
3257 Clockwork.BackgroundBlurs[panel] = nil;
3258 end;
3259
3260 -- A function to draw the background blurs.
3261 function Clockwork.kernel:DrawBackgroundBlurs()
3262 local scrH, scrW = ScrH(), ScrW();
3263 local sysTime = SysTime();
3264
3265 for k, v in pairs(Clockwork.BackgroundBlurs) do
3266 if (type(k) == "string" or (IsValid(k) and k:IsVisible())) then
3267 local fraction = mathClamp((sysTime - v) / 1, 0, 1);
3268 local x, y = 0, 0;
3269
3270 surface.SetMaterial(Clockwork.ScreenBlur);
3271 surface.SetDrawColor(255, 255, 255, 255);
3272
3273 for i = 0.33, 1, 0.33 do
3274 Clockwork.ScreenBlur:SetFloat("$blur", fraction * 5 * i);
3275 Clockwork.ScreenBlur:Recompute();
3276
3277 if (render) then render.UpdateScreenEffectTexture();end;
3278
3279 surface.DrawTexturedRect(x, y, scrW, scrH);
3280 end;
3281
3282 surface.SetDrawColor(10, 10, 10, 200 * fraction);
3283 surface.DrawRect(x, y, scrW, scrH);
3284 end;
3285 end;
3286 end;
3287
3288 -- A function to get the notice panel.
3289 function Clockwork.kernel:GetNoticePanel()
3290 if (IsValid(Clockwork.NoticePanel) and Clockwork.NoticePanel:IsVisible()) then
3291 return Clockwork.NoticePanel;
3292 end;
3293 end;
3294
3295 -- A function to set the notice panel.
3296 function Clockwork.kernel:SetNoticePanel(noticePanel)
3297 Clockwork.NoticePanel = noticePanel;
3298 end;
3299
3300 -- A function to add some cinematic text.
3301 function Clockwork.kernel:AddCinematicText(text, color, barLength, hangTime, font, bThisOnly)
3302 local colorWhite = Clockwork.option:GetColor("white");
3303 local cinematicTable = {
3304 barLength = barLength or (ScrH() * 8),
3305 hangTime = hangTime or 3,
3306 color = color or colorWhite,
3307 font = font,
3308 text = text,
3309 add = 0
3310 };
3311
3312 if (bThisOnly) then
3313 Clockwork.Cinematics[1] = cinematicTable;
3314 else
3315 Clockwork.Cinematics[#Clockwork.Cinematics + 1] = cinematicTable;
3316 end;
3317 end;
3318
3319 -- A function to add a notice.
3320 function Clockwork.kernel:AddNotify(text, class, length)
3321 if (class != NOTIFY_HINT or stringSub(text, 1, 6) != "#Hint_") then
3322 if (Clockwork.BaseClass.AddNotify) then
3323 Clockwork.BaseClass:AddNotify(text, class, length);
3324 end;
3325 end;
3326 end;
3327
3328 -- A function to get whether the local player is using the tool gun.
3329 function Clockwork.kernel:IsUsingTool()
3330 if (IsValid(Clockwork.Client:GetActiveWeapon())
3331 and Clockwork.Client:GetActiveWeapon():GetClass() == "gmod_tool") then
3332 return true;
3333 else
3334 return false;
3335 end;
3336 end;
3337
3338 -- A function to get whether the local player is using the camera.
3339 function Clockwork.kernel:IsUsingCamera()
3340 if (IsValid(Clockwork.Client:GetActiveWeapon())
3341 and Clockwork.Client:GetActiveWeapon():GetClass() == "gmod_camera") then
3342 return true;
3343 else
3344 return false;
3345 end;
3346 end;
3347
3348 -- A function to get the target ID data.
3349 function Clockwork.kernel:GetTargetIDData()
3350 return Clockwork.TargetIDData;
3351 end;
3352
3353 -- A function to calculate the screen fading.
3354 function Clockwork.kernel:CalculateScreenFading()
3355 if (Clockwork.plugin:Call("ShouldPlayerScreenFadeBlack")) then
3356 if (!Clockwork.BlackFadeIn) then
3357 if (Clockwork.BlackFadeOut) then
3358 Clockwork.BlackFadeIn = Clockwork.BlackFadeOut;
3359 else
3360 Clockwork.BlackFadeIn = 0;
3361 end;
3362 end;
3363
3364 Clockwork.BlackFadeIn = mathClamp(Clockwork.BlackFadeIn + (FrameTime() * 20), 0, 255);
3365 Clockwork.BlackFadeOut = nil;
3366 self:DrawSimpleGradientBox(0, 0, 0, ScrW(), ScrH(), Color(0, 0, 0, Clockwork.BlackFadeIn));
3367 else
3368 if (Clockwork.BlackFadeIn) then
3369 Clockwork.BlackFadeOut = Clockwork.BlackFadeIn;
3370 end;
3371
3372 Clockwork.BlackFadeIn = nil;
3373
3374 if (Clockwork.BlackFadeOut) then
3375 Clockwork.BlackFadeOut = mathClamp(Clockwork.BlackFadeOut - (FrameTime() * 40), 0, 255);
3376 self:DrawSimpleGradientBox(0, 0, 0, ScrW(), ScrH(), Color(0, 0, 0, Clockwork.BlackFadeOut));
3377
3378 if (Clockwork.BlackFadeOut == 0) then
3379 Clockwork.BlackFadeOut = nil;
3380 end;
3381 end;
3382 end;
3383 end;
3384
3385 -- A function to draw a cinematic.
3386 function Clockwork.kernel:DrawCinematic(cinematicTable, curTime)
3387 local maxBarLength = cinematicTable.barLength or (ScrH() / 13);
3388 local font = cinematicTable.font or Clockwork.option:GetFont("cinematic_text");
3389
3390 if (cinematicTable.goBack and curTime > cinematicTable.goBack) then
3391 cinematicTable.add = mathClamp(cinematicTable.add - 2, 0, maxBarLength);
3392
3393 if (cinematicTable.add == 0) then
3394 tableRemove(Clockwork.Cinematics, 1);
3395 cinematicTable = nil;
3396 end;
3397 else
3398 cinematicTable.add = mathClamp(cinematicTable.add + 1, 0, maxBarLength);
3399
3400 if (cinematicTable.add == maxBarLength and !cinematicTable.goBack) then
3401 cinematicTable.goBack = curTime + cinematicTable.hangTime;
3402 end;
3403 end;
3404
3405 if (cinematicTable) then
3406 draw.RoundedBox(0, 0, -maxBarLength + cinematicTable.add, ScrW(), maxBarLength, Color(0, 0, 0, 255));
3407 draw.RoundedBox(0, 0, ScrH() - cinematicTable.add, ScrW(), maxBarLength, Color(0, 0, 0, 255));
3408 draw.SimpleText(cinematicTable.text, font, ScrW() / 2, (ScrH() - cinematicTable.add) + (maxBarLength / 2), cinematicTable.color, 1, 1);
3409 end
3410 end;
3411
3412 -- A function to draw the cinematic introduction.
3413 function Clockwork.kernel:DrawCinematicIntro(curTime)
3414 local cinematicInfo = Clockwork.plugin:Call("GetCinematicIntroInfo");
3415 local colorWhite = Clockwork.option:GetColor("white");
3416
3417 if (cinematicInfo) then
3418 if (Clockwork.CinematicScreenAlpha and Clockwork.CinematicScreenTarget) then
3419 Clockwork.CinematicScreenAlpha = mathApproach(Clockwork.CinematicScreenAlpha, Clockwork.CinematicScreenTarget, 1);
3420
3421 if (Clockwork.CinematicScreenAlpha == Clockwork.CinematicScreenTarget) then
3422 if (Clockwork.CinematicScreenTarget == 255) then
3423 if (!Clockwork.CinematicScreenGoBack) then
3424 Clockwork.CinematicScreenGoBack = curTime + 2.5;
3425 Clockwork.option:PlaySound("rollover");
3426 end;
3427 else
3428 Clockwork.CinematicScreenDone = true;
3429 end;
3430 end;
3431
3432 if (Clockwork.CinematicScreenGoBack and curTime >= Clockwork.CinematicScreenGoBack) then
3433 Clockwork.CinematicScreenGoBack = nil;
3434 Clockwork.CinematicScreenTarget = 0;
3435 Clockwork.option:PlaySound("rollover");
3436 end;
3437
3438 if (!Clockwork.CinematicScreenDone and cinematicInfo.credits) then
3439 local alpha = mathClamp(Clockwork.CinematicScreenAlpha, 0, 255);
3440
3441 self:OverrideMainFont(Clockwork.option:GetFont("intro_text_tiny"));
3442 self:DrawSimpleText(cinematicInfo.credits, ScrW() / 8, ScrH() * 0.75, Color(colorWhite.r, colorWhite.g, colorWhite.b, alpha));
3443 self:OverrideMainFont(false);
3444 end;
3445 else
3446 Clockwork.CinematicScreenAlpha = 0;
3447 Clockwork.CinematicScreenTarget = 255;
3448 Clockwork.option:PlaySound("rollover");
3449 end;
3450 end;
3451 end;
3452
3453 -- A function to draw the cinematic introduction bars.
3454 function Clockwork.kernel:DrawCinematicIntroBars()
3455 if (Clockwork.config:Get("draw_intro_bars"):Get()) then
3456 local maxBarLength = ScrH() / 8;
3457
3458 if (!Clockwork.CinematicBarsTarget and !Clockwork.CinematicBarsAlpha) then
3459 Clockwork.CinematicBarsAlpha = 0;
3460 Clockwork.CinematicBarsTarget = 255;
3461 Clockwork.option:PlaySound("rollover");
3462 end;
3463
3464 Clockwork.CinematicBarsAlpha = mathApproach(Clockwork.CinematicBarsAlpha, Clockwork.CinematicBarsTarget, 1);
3465
3466 if (Clockwork.CinematicScreenDone) then
3467 if (Clockwork.CinematicScreenBarLength != 0) then
3468 Clockwork.CinematicScreenBarLength = mathClamp((maxBarLength / 255) * Clockwork.CinematicBarsAlpha, 0, maxBarLength);
3469 end;
3470
3471 if (Clockwork.CinematicBarsTarget != 0) then
3472 Clockwork.CinematicBarsTarget = 0;
3473 Clockwork.option:PlaySound("rollover");
3474 end;
3475
3476 if (Clockwork.CinematicBarsAlpha == 0) then
3477 Clockwork.CinematicBarsDrawn = true;
3478 end;
3479 elseif (Clockwork.CinematicScreenBarLength != maxBarLength) then
3480 if (!Clockwork.IntroBarsMultiplier) then
3481 Clockwork.IntroBarsMultiplier = 1;
3482 else
3483 Clockwork.IntroBarsMultiplier = mathClamp(Clockwork.IntroBarsMultiplier + (FrameTime() * 8), 1, 12);
3484 end;
3485
3486 Clockwork.CinematicScreenBarLength = mathClamp((maxBarLength / 255) * mathClamp(Clockwork.CinematicBarsAlpha * Clockwork.IntroBarsMultiplier, 0, 255), 0, maxBarLength);
3487 end;
3488
3489 draw.RoundedBox(0, 0, 0, ScrW(), Clockwork.CinematicScreenBarLength, Color(0, 0, 0, 255));
3490 draw.RoundedBox(0, 0, ScrH() - Clockwork.CinematicScreenBarLength, ScrW(), maxBarLength, Color(0, 0, 0, 255));
3491 end;
3492 end;
3493
3494 -- A function to draw the cinematic info.
3495 function Clockwork.kernel:DrawCinematicInfo()
3496 if (!Clockwork.CinematicInfoAlpha and !Clockwork.CinematicInfoSlide) then
3497 Clockwork.CinematicInfoAlpha = 255;
3498 Clockwork.CinematicInfoSlide = 0;
3499 end;
3500
3501 Clockwork.CinematicInfoSlide = mathApproach(Clockwork.CinematicInfoSlide, 255, 1);
3502
3503 if (Clockwork.CinematicScreenAlpha and Clockwork.CinematicScreenTarget) then
3504 Clockwork.CinematicInfoAlpha = mathApproach(Clockwork.CinematicInfoAlpha, 0, 1);
3505
3506 if (Clockwork.CinematicInfoAlpha == 0) then
3507 Clockwork.CinematicInfoDrawn = true;
3508 end;
3509 end;
3510
3511 local cinematicInfo = Clockwork.plugin:Call("GetCinematicIntroInfo");
3512 local colorWhite = Clockwork.option:GetColor("white");
3513 local colorInfo = Clockwork.option:GetColor("information");
3514
3515 if (cinematicInfo) then
3516 local screenHeight = ScrH();
3517 local screenWidth = ScrW();
3518 local textPosScale = 1 - (Clockwork.CinematicInfoAlpha / 255);
3519 local textPosY = (screenHeight * 0.35) - ((screenHeight * 0.15) * textPosScale);
3520 local textPosX = screenWidth * 0.3;
3521
3522 if (cinematicInfo.title) then
3523 local cinematicInfoTitle = stringUpper(cinematicInfo.title);
3524 local cinematicIntroText = stringUpper(cinematicInfo.text);
3525 local introTextSmallFont = Clockwork.option:GetFont("intro_text_small");
3526 local introTextBigFont = Clockwork.option:GetFont("intro_text_big");
3527 local textWidth, textHeight = self:GetCachedTextSize(introTextBigFont, cinematicInfoTitle);
3528 local boxAlpha = mathMin(Clockwork.CinematicInfoAlpha, 150);
3529
3530 if (cinematicInfo.text) then
3531 local smallTextWidth, smallTextHeight = self:GetCachedTextSize(introTextSmallFont, cinematicIntroText);
3532
3533 self:DrawGradient(
3534 GRADIENT_RIGHT, 0, textPosY - 80, screenWidth, textHeight + smallTextHeight + 160, Color(100, 100, 100, boxAlpha)
3535 );
3536 else
3537 self:DrawGradient(
3538 GRADIENT_RIGHT, 0, textPosY - 80, screenWidth, textHeight + 160, Color(100, 100, 100, boxAlpha)
3539 );
3540 end;
3541
3542 self:OverrideMainFont(introTextBigFont);
3543 self:DrawSimpleText(cinematicInfoTitle, textPosX, textPosY, Color(colorInfo.r, colorInfo.g, colorInfo.b, Clockwork.CinematicInfoAlpha), nil, nil, true);
3544 self:OverrideMainFont(false);
3545
3546 if (cinematicInfo.text) then
3547 self:OverrideMainFont(introTextSmallFont);
3548 self:DrawSimpleText(cinematicIntroText, textPosX, textPosY + textHeight + 8, Color(colorWhite.r, colorWhite.g, colorWhite.b, Clockwork.CinematicInfoAlpha), nil, nil, true);
3549 self:OverrideMainFont(false);
3550 end;
3551 elseif (cinematicInfo.text) then
3552 self:OverrideMainFont(introTextSmallFont);
3553 self:DrawSimpleText(cinematicIntroText, textPosX, textPosY, Color(colorWhite.r, colorWhite.g, colorWhite.b, Clockwork.CinematicInfoAlpha), nil, nil, true);
3554 self:OverrideMainFont(false);
3555 end;
3556 end;
3557 end;
3558
3559 -- A function to draw some door text.
3560 function Clockwork.kernel:DrawDoorText(entity, eyePos, eyeAngles, font, nameColor, textColor)
3561 local entityColor = entity:GetColor();
3562
3563 if (entityColor.a <= 0 or entity:IsEffectActive(EF_NODRAW)) then
3564 return;
3565 end;
3566
3567 local doorData = Clockwork.entity:CalculateDoorTextPosition(entity);
3568
3569 if (!doorData.hitWorld) then
3570 local frontY = -26;
3571 local backY = -26;
3572 local alpha = self:CalculateAlphaFromDistance(256, eyePos, entity:GetPos());
3573
3574 if (alpha <= 0) then
3575 return;
3576 end;
3577
3578 local owner = Clockwork.entity:GetOwner(entity);
3579 local name = Clockwork.plugin:Call("GetDoorInfo", entity, DOOR_INFO_NAME);
3580 local text = Clockwork.plugin:Call("GetDoorInfo", entity, DOOR_INFO_TEXT);
3581
3582 if (name or text) then
3583 local nameWidth, nameHeight = self:GetCachedTextSize(font, name or "");
3584 local textWidth, textHeight = self:GetCachedTextSize(font, text or "");
3585 local longWidth = nameWidth;
3586 local boxAlpha = mathMin(alpha, 150);
3587
3588 if (textWidth > longWidth) then
3589 longWidth = textWidth;
3590 end;
3591
3592 local scale = mathAbs((doorData.width * 0.75) / longWidth);
3593 local nameScale = mathMin(scale, 0.05);
3594 local textScale = mathMin(scale, 0.03);
3595 local longHeight = nameHeight + textHeight + 8;
3596
3597 cam.Start3D2D(doorData.position, doorData.angles, nameScale);
3598 self:DrawGradient(GRADIENT_CENTER, -(longWidth / 2) - 128, frontY - 8, longWidth + 256, longHeight, Color(100, 100, 100, boxAlpha));
3599 cam.End3D2D();
3600
3601 cam.Start3D2D(doorData.positionBack, doorData.anglesBack, nameScale);
3602 self:DrawGradient(GRADIENT_CENTER, -(longWidth / 2) - 128, frontY - 8, longWidth + 256, longHeight, Color(100, 100, 100, boxAlpha));
3603 cam.End3D2D();
3604
3605 if (name) then
3606 if (!text or text == "") then
3607 nameColor = textColor or nameColor;
3608 end;
3609
3610 cam.Start3D2D(doorData.position, doorData.angles, nameScale);
3611 self:OverrideMainFont(font);
3612 frontY = self:DrawInfo(name, 0, frontY, nameColor, alpha, nil, nil, 3);
3613 self:OverrideMainFont(false);
3614 cam.End3D2D();
3615
3616 cam.Start3D2D(doorData.positionBack, doorData.anglesBack, nameScale);
3617 self:OverrideMainFont(font);
3618 backY = self:DrawInfo(name, 0, backY, nameColor, alpha, nil, nil, 3);
3619 self:OverrideMainFont(false);
3620 cam.End3D2D();
3621 end;
3622
3623 if (text) then
3624 cam.Start3D2D(doorData.position, doorData.angles, textScale);
3625 self:OverrideMainFont(font);
3626 frontY = self:DrawInfo(text, 0, frontY, textColor, alpha, nil, nil, 3);
3627 self:OverrideMainFont(false);
3628 cam.End3D2D();
3629
3630 cam.Start3D2D(doorData.positionBack, doorData.anglesBack, textScale);
3631 self:OverrideMainFont(font);
3632 backY = self:DrawInfo(text, 0, backY, textColor, alpha, nil, nil, 3);
3633 self:OverrideMainFont(false);
3634 cam.End3D2D();
3635 end;
3636 end;
3637 end;
3638 end;
3639
3640 -- A function to get whether the local player's character screen is open.
3641 function Clockwork.kernel:IsCharacterScreenOpen(isVisible)
3642 if (Clockwork.character:IsPanelOpen()) then
3643 local panel = Clockwork.character:GetPanel();
3644
3645 if (isVisible) then
3646 if (panel) then
3647 return panel:IsVisible();
3648 end;
3649 else
3650 return panel != nil;
3651 end;
3652 end;
3653 end;
3654
3655 -- A function to save schema data.
3656 function Clockwork.kernel:SaveSchemaData(fileName, data)
3657 if (type(data) != "table") then
3658 MsgC(Color(255, 100, 0, 255), "[Clockwork:Kernel] The '"..fileName.."' schema data has failed to save.\nUnable to save type "..type(data)..", table required.\n");
3659
3660 return;
3661 end;
3662
3663 _file.Write("clockwork/schemas/"..self:GetSchemaFolder().."/"..fileName..".txt", self:Serialize(data));
3664 end;
3665
3666 -- A function to delete schema data.
3667 function Clockwork.kernel:DeleteSchemaData(fileName)
3668 _file.Delete("clockwork/schemas/"..self:GetSchemaFolder().."/"..fileName..".txt");
3669 end;
3670
3671 -- A function to check if schema data exists.
3672 function Clockwork.kernel:SchemaDataExists(fileName)
3673 return _file.Exists("clockwork/schemas/"..self:GetSchemaFolder().."/"..fileName..".txt", "DATA");
3674 end;
3675
3676 -- A function to find schema data in a directory.
3677 function Clockwork.kernel:FindSchemaDataInDir(directory)
3678 return _file.Find("clockwork/schemas/"..self:GetSchemaFolder().."/"..directory, "LUA", "namedesc");
3679 end;
3680
3681 -- A function to restore schema data.
3682 function Clockwork.kernel:RestoreSchemaData(fileName, failSafe)
3683 if (self:SchemaDataExists(fileName)) then
3684 local data = _file.Read("clockwork/schemas/"..self:GetSchemaFolder().."/"..fileName..".txt", "DATA");
3685
3686 if (data) then
3687 local bSuccess, value = pcall(util.JSONToTable, data);
3688
3689 if (bSuccess and value != nil) then
3690 return value;
3691 else
3692 local bSuccess, value = pcall(self.Deserialize, self, data);
3693
3694 if (bSuccess and value != nil) then
3695 return value;
3696 else
3697 MsgC(Color(255, 100, 0, 255), "[Clockwork:Kernel] '"..fileName.."' schema data has failed to restore.\n"..value.."\n");
3698
3699 self:DeleteSchemaData(fileName);
3700 end;
3701 end;
3702 end;
3703 end;
3704
3705 if (failSafe != nil) then
3706 return failSafe;
3707 else
3708 return {};
3709 end;
3710 end;
3711
3712 -- A function to restore Clockwork data.
3713 function Clockwork.kernel:RestoreClockworkData(fileName, failSafe)
3714 if (self:ClockworkDataExists(fileName)) then
3715 local data = _file.Read("clockwork/"..fileName..".txt", "DATA");
3716
3717 if (data) then
3718 local success, value = pcall(util.JSONToTable, data);
3719
3720 if (success and value != nil) then
3721 return value;
3722 else
3723 local bSuccess, value = pcall(self.Deserialize, self, data);
3724
3725 if (bSuccess and value != nil) then
3726 return value;
3727 else
3728 MsgC(Color(255, 100, 0, 255), "[Clockwork:Kernel] '"..fileName.."' clockwork data has failed to restore.\n"..value.."\n");
3729
3730 self:DeleteClockworkData(fileName);
3731 end;
3732 end;
3733 end;
3734 end;
3735
3736 if (failSafe != nil) then
3737 return failSafe;
3738 else
3739 return {};
3740 end;
3741 end;
3742
3743 -- A function to save Clockwork data.
3744 function Clockwork.kernel:SaveClockworkData(fileName, data)
3745 if (type(data) != "table") then
3746 MsgC(Color(255, 100, 0, 255), "[Clockwork:Kernel] The '"..fileName.."' clockwork data has failed to save.\nUnable to save type "..type(data)..", table required.\n");
3747
3748 return;
3749 end;
3750
3751 _file.Write("clockwork/"..fileName..".txt", self:Serialize(data));
3752 end;
3753
3754 -- A function to check if Clockwork data exists.
3755 function Clockwork.kernel:ClockworkDataExists(fileName)
3756 return _file.Exists("clockwork/"..fileName..".txt", "DATA");
3757 end;
3758
3759 -- A function to delete Clockwork data.
3760 function Clockwork.kernel:DeleteClockworkData(fileName)
3761 _file.Delete("clockwork/"..fileName..".txt");
3762 end;
3763
3764 -- A function to run a Clockwork command.
3765 function Clockwork.kernel:RunCommand(command, ...)
3766 RunConsoleCommand("cwCmd", command, ...);
3767 end;
3768
3769 -- A function to get whether the local player is choosing a character.
3770 function Clockwork.kernel:IsChoosingCharacter()
3771 if (Clockwork.character:GetPanel()) then
3772 return Clockwork.character:IsPanelOpen();
3773 else
3774 return true;
3775 end;
3776 end;
3777
3778 -- A function to include the schema.
3779 function Clockwork.kernel:IncludeSchema()
3780 local schemaFolder = self:GetSchemaFolder();
3781
3782 if (schemaFolder and type(schemaFolder) == "string") then
3783 Clockwork.plugin:Include(schemaFolder.."/schema", true);
3784 end;
3785 end;
3786end;
3787
3788-- A function to explode a string by tags.
3789function Clockwork.kernel:ExplodeByTags(text, seperator, open, close, hide)
3790 local results = {};
3791 local current = "";
3792 local tag = nil;
3793
3794 for i = 1, #text do
3795 local character = stringSub(text, i, i);
3796
3797 if (!tag) then
3798 if (character == open) then
3799 if (!hide) then
3800 current = current..character;
3801 end;
3802
3803 tag = true;
3804 elseif (character == seperator) then
3805 results[#results + 1] = current; current = "";
3806 else
3807 current = current..character;
3808 end;
3809 else
3810 if (character == close) then
3811 if (!hide) then
3812 current = current..character;
3813 end;
3814
3815 tag = nil;
3816 else
3817 current = current..character;
3818 end;
3819 end;
3820 end;
3821
3822 if (current != "") then
3823 results[#results + 1] = current;
3824 end;
3825
3826 return results;
3827end;
3828
3829-- A function to modify a physical description.
3830function Clockwork.kernel:ModifyPhysDesc(description)
3831 if (stringLen(description) <= 128) then
3832 if (!stringFind(stringSub(description, -2), "%p")) then
3833 return description..".";
3834 else
3835 return description;
3836 end;
3837 else
3838 return stringSub(description, 1, 125).."...";
3839 end;
3840end;
3841
3842local MAGIC_CHARACTERS = "([%(%)%.%%%+%-%*%?%[%^%$])";
3843
3844-- A function to replace something in text without pattern matching.
3845function Clockwork.kernel:Replace(text, find, replace)
3846 return (text:gsub(find:gsub(MAGIC_CHARACTERS, "%%%1"), replace));
3847end;
3848
3849-- A function to create a new meta table.
3850function Clockwork.kernel:NewMetaTable(baseTable)
3851 local object = {};
3852 setmetatable(object, baseTable);
3853 baseTable.__index = baseTable;
3854 return object;
3855end;
3856
3857-- A function to make a proxy meta table.
3858function Clockwork.kernel:MakeProxyTable(baseTable, baseClass, proxy)
3859 baseTable[proxy] = {};
3860
3861 baseTable.__index = function(object, key)
3862 local value = rawget(object, key);
3863
3864 if (type(value) == "function") then
3865 return value;
3866 elseif (object.__proxy) then
3867 return object:__proxy(key);
3868 else
3869 return object[proxy][key];
3870 end;
3871 end;
3872
3873 baseTable.__newindex = function(object, key, value)
3874 if (type(value) ~= "function") then
3875 object[proxy][key] = value;
3876 return;
3877 end;
3878
3879 rawset(object, key, value);
3880 end;
3881
3882 for k, v in pairs(baseTable) do
3883 if (type(v) ~= "function" and k ~= proxy) then
3884 baseTable[proxy][k] = v;
3885 baseTable[k] = nil;
3886 end;
3887 end;
3888
3889 setmetatable(baseTable, baseClass);
3890end;
3891
3892-- A function to set whether a string should be in camel case.
3893function Clockwork.kernel:SetCamelCase(text, bCamelCase)
3894 if (bCamelCase) then
3895 return stringGsub(text, "^.", stringLower);
3896 else
3897 return stringGsub(text, "^.", stringUpper);
3898 end;
3899end;
3900
3901-- A function to add files to the content download.
3902function Clockwork.kernel:AddDirectory(directory, bRecursive)
3903 if (stringSub(directory, -1) == "/") then
3904 directory = directory.."*.*";
3905 end;
3906
3907 local files, folders = _file.Find(directory, "GAME", "namedesc");
3908 local rawDirectory = stringMatch(directory, "(.*)/").."/";
3909
3910 for k, v in pairs(files) do
3911 self:AddFile(rawDirectory..v);
3912 end;
3913
3914 if (bRecursive) then
3915 for k, v in pairs(folders) do
3916 if (v != ".." and v != ".") then
3917 self:AddDirectory(rawDirectory..v, true);
3918 end;
3919 end;
3920 end;
3921end;
3922
3923-- A function to add a file to the content download.
3924function Clockwork.kernel:AddFile(fileName)
3925 if (_file.Exists(fileName, "GAME")) then
3926 resource.AddFile(fileName);
3927 else
3928 -- print(Format("[Clockwork] File does not exist: %s.", fileName));
3929 end;
3930end;
3931
3932-- A function to include files in a directory.
3933function Clockwork.kernel:IncludeDirectory(directory, bFromBase)
3934 if (bFromBase) then
3935 directory = "clockwork/framework/"..directory;
3936 end;
3937
3938 if (stringSub(directory, -1) != "/") then
3939 directory = directory.."/";
3940 end;
3941
3942 for k, v in pairs(_file.Find(directory.."*.lua", "LUA", "namedesc")) do
3943 self:IncludePrefixed(directory..v);
3944 end;
3945end;
3946
3947-- A function to include a prefixed cwFile.
3948function Clockwork.kernel:IncludePrefixed(fileName)
3949 local isShared = (stringFind(fileName, "sh_") or stringFind(fileName, "shared.lua"));
3950 local isClient = (stringFind(fileName, "cl_") or stringFind(fileName, "cl_init.lua"));
3951 local isServer = (stringFind(fileName, "sv_"));
3952
3953 if (isServer and !SERVER) then
3954 return;
3955 end;
3956
3957 if (isShared and SERVER) then
3958 AddCSLuaFile(fileName);
3959 elseif (isClient and SERVER) then
3960 AddCSLuaFile(fileName);
3961 return;
3962 end;
3963
3964 local success, err = pcall(include, fileName);
3965
3966 if (!success) then
3967 MsgN("[Clockwork] File System -> "..err);
3968 end;
3969end;
3970
3971-- A function to include plugins in a directory.
3972function Clockwork.kernel:IncludePlugins(directory, bFromBase)
3973 if (bFromBase) then
3974 directory = "Clockwork/"..directory;
3975 end;
3976
3977 if (stringSub(directory, -1) != "/") then
3978 directory = directory.."/";
3979 end;
3980
3981 local files, pluginFolders = _file.Find(directory.."*", "LUA", "namedesc");
3982
3983 for k, v in pairs(pluginFolders) do
3984 if (v != ".." and v != ".") then
3985 Clockwork.plugin:Include(directory..v.."/plugin");
3986 end;
3987 end;
3988
3989 return true;
3990end;
3991
3992-- A function to perform the timer think.
3993function Clockwork.kernel:CallTimerThink(curTime)
3994 for k, v in pairs(Clockwork.Timers) do
3995 if (!v.paused) then
3996 if (curTime >= v.nextCall) then
3997 local bSuccess, value = pcall(v.Callback, unpack(v.arguments));
3998
3999 if (!bSuccess) then
4000 MsgC(Color(255, 100, 0, 255), "[Clockwork:Kernel] The '"..tostring(k).."' timer has failed to run.\n"..value.."\n");
4001 end;
4002
4003 v.nextCall = curTime + v.delay;
4004 v.calls = v.calls + 1;
4005
4006 if (v.calls == v.repetitions) then
4007 Clockwork.Timers[k] = nil;
4008 end;
4009 end;
4010 end;
4011 end;
4012end;
4013
4014-- A function to get whether a timer exists.
4015function Clockwork.kernel:TimerExists(name)
4016 return Clockwork.Timers[name];
4017end;
4018
4019-- A function to start a timer.
4020function Clockwork.kernel:StartTimer(name)
4021 if (Clockwork.Timers[name] and Clockwork.Timers[name].paused) then
4022 Clockwork.Timers[name].nextCall = CurTime() + Clockwork.Timers[name].timeLeft;
4023 Clockwork.Timers[name].paused = nil;
4024 end;
4025end;
4026
4027-- A function to pause a timer.
4028function Clockwork.kernel:PauseTimer(name)
4029 if (Clockwork.Timers[name] and !Clockwork.Timers[name].paused) then
4030 Clockwork.Timers[name].timeLeft = Clockwork.Timers[name].nextCall - CurTime();
4031 Clockwork.Timers[name].paused = true;
4032 end;
4033end;
4034
4035-- A function to destroy a timer.
4036function Clockwork.kernel:DestroyTimer(name)
4037 Clockwork.Timers[name] = nil;
4038end;
4039
4040-- A function to create a timer.
4041function Clockwork.kernel:CreateTimer(name, delay, repetitions, Callback, ...)
4042 Clockwork.Timers[name] = {
4043 calls = 0,
4044 delay = delay,
4045 nextCall = CurTime() + delay,
4046 Callback = Callback,
4047 arguments = {...},
4048 repetitions = repetitions
4049 };
4050end;
4051
4052-- A function to run a function on the next frame.
4053function Clockwork.kernel:OnNextFrame(name, Callback)
4054 self:CreateTimer(name, FrameTime(), 1, Callback);
4055end;
4056
4057-- A function to get whether a player has access to an object.
4058function Clockwork.kernel:HasObjectAccess(player, object)
4059 local hasAccess = false;
4060 local faction = player:GetFaction();
4061
4062 if (object.access) then
4063 if (Clockwork.player:HasAnyFlags(player, object.access)) then
4064 hasAccess = true;
4065 end;
4066 end;
4067
4068 if (object.factions) then
4069 if (tableHasValue(object.factions, faction)) then
4070 hasAccess = true;
4071 end;
4072 end;
4073
4074 if (object.classes) then
4075 local team = player:Team();
4076 local class = Clockwork.class:FindByID(team);
4077
4078 if (class) then
4079 if (tableHasValue(object.classes, team)
4080 or tableHasValue(object.classes, class.name)) then
4081 hasAccess = true;
4082 end;
4083 end;
4084 end;
4085
4086 if (!object.access and !object.factions
4087 and !object.classes) then
4088 hasAccess = true;
4089 end;
4090
4091 if (object.blacklist) then
4092 local team = player:Team();
4093 local class = Clockwork.class:FindByID(team);
4094
4095 if (tableHasValue(object.blacklist, faction)) then
4096 hasAccess = false;
4097 elseif (class) then
4098 if (tableHasValue(object.blacklist, team)
4099 or tableHasValue(object.blacklist, class.name)) then
4100 hasAccess = false;
4101 end;
4102 else
4103 for k, v in pairs(object.blacklist) do
4104 if (type(v) == "string") then
4105 if (Clockwork.player:HasAnyFlags(player, v)) then
4106 hasAccess = false;
4107
4108 break;
4109 end;
4110 end;
4111 end;
4112 end;
4113 end;
4114
4115 if (object.HasObjectAccess) then
4116 return object:HasObjectAccess(player, hasAccess);
4117 end;
4118
4119 return hasAccess;
4120end;
4121
4122-- A function to get the sorted commands.
4123function Clockwork.kernel:GetSortedCommands()
4124 local commands = {};
4125 local source = Clockwork.command.stored;
4126
4127 for k, v in pairs(source) do
4128 commands[#commands + 1] = k;
4129 end;
4130
4131 tableSort(commands, function(a, b)
4132 return a < b;
4133 end);
4134
4135 return commands;
4136end;
4137
4138-- A function to zero a number to an amount of digits.
4139function Clockwork.kernel:ZeroNumberToDigits(number, digits)
4140 return stringRep("0", mathClamp(digits - stringLen(tostring(number)), 0, digits))..tostring(number);
4141end;
4142
4143-- A function to get a short CRC from a value.
4144function Clockwork.kernel:GetShortCRC(value)
4145 return mathCeil(util.CRC(value) / 100000);
4146end;
4147
4148-- A function to validate a table's keys.
4149function Clockwork.kernel:ValidateTableKeys(baseTable)
4150 for i = 1, #baseTable do
4151 if (!baseTable[i]) then
4152 tableRemove(baseTable, i);
4153 end;
4154 end;
4155end;
4156
4157-- A function to get the map's physics entities.
4158function Clockwork.kernel:GetPhysicsEntities()
4159 local entities = {};
4160
4161 for k, v in pairs(ents.FindByClass("prop_physicsmultiplayer")) do
4162 if (IsValid(v)) then
4163 entities[#entities + 1] = v;
4164 end;
4165 end;
4166
4167 for k, v in pairs(ents.FindByClass("prop_physics")) do
4168 if (IsValid(v)) then
4169 entities[#entities + 1] = v;
4170 end;
4171 end;
4172
4173 return entities;
4174end;
4175
4176-- A function to create a multicall table (by Deco Da Man).
4177function Clockwork.kernel:CreateMulticallTable(baseTable, object)
4178 local metaTable = getmetatable(baseTable) or {};
4179 function metaTable.__index(baseTable, key)
4180 return function(baseTable, ...)
4181 for k, v in pairs(baseTable) do
4182 object[key](v, ...);
4183 end;
4184 end
4185 end
4186 setmetatable(baseTable, metaTable);
4187
4188 return baseTable;
4189end;
4190
4191local NETWORKED_VALUE_TABLE = {
4192 [NWTYPE_STRING] = "",
4193 [NWTYPE_ENTITY] = NULL,
4194 [NWTYPE_VECTOR] = Vector(0, 0, 0),
4195 [NWTYPE_NUMBER] = 0,
4196 [NWTYPE_ANGLE] = Angle(0, 0, 0),
4197 [NWTYPE_FLOAT] = 0.0,
4198 [NWTYPE_BOOL] = false
4199};
4200
4201-- A function to get a default networked value.
4202function Clockwork.kernel:GetDefaultNetworkedValue(class)
4203 return NETWORKED_VALUE_TABLE[class];
4204end;
4205
4206local NETWORKED_CLASS_TABLE = {
4207 [NWTYPE_STRING] = "String",
4208 [NWTYPE_ENTITY] = "Entity",
4209 [NWTYPE_VECTOR] = "Vector",
4210 [NWTYPE_NUMBER] = "Int",
4211 [NWTYPE_ANGLE] = "Angle",
4212 [NWTYPE_FLOAT] = "Float",
4213 [NWTYPE_BOOL] = "Bool"
4214};
4215
4216-- A function to convert a networked class.
4217function Clockwork.kernel:ConvertNetworkedClass(class)
4218 return NETWORKED_CLASS_TABLE[class];
4219end;
4220
4221local DEFAULT_NETWORK_CLASS_VALUE = {
4222 ["String"] = "",
4223 ["Entity"] = NULL,
4224 ["Vector"] = Vector(0, 0, 0),
4225 ["Int"] = 0,
4226 ["Angle"] = Angle(0, 0, 0),
4227 ["Float"] = 0.0,
4228 ["Bool"] = false
4229};
4230
4231-- A function to get the default class value.
4232function Clockwork.kernel:GetDefaultClassValue(class)
4233 return DEFAULT_NETWORK_CLASS_VALUE[class];
4234end;
4235
4236-- A function to set a shared variable.
4237function Clockwork.kernel:SetSharedVar(key, value, sharedTable)
4238 if (!sharedTable) then
4239 local sharedVars = self:GetSharedVars():Global();
4240
4241 if (sharedVars and sharedVars[key]) then
4242 local class = self:ConvertNetworkedClass(sharedVars[key].class);
4243 if (class) then
4244 if (value == nil) then
4245 value = self:GetDefaultClassValue(class);
4246 end;
4247 local success, err = pcall(_G["SetGlobal"..class], key, value);
4248 if (!success) then
4249 MsgC(Color(255, 100, 0, 255), "[Clockwork:GlobalSharedVars] Attempted to set SharedVar '"..key.."'' of type '"..class.."'' with value of type '"..type(value).."'.\n"..err.."\n");
4250 end;
4251 return;
4252 end;
4253 end;
4254 else
4255 Clockwork.SharedTables[sharedTable] = Clockwork.SharedTables[sharedTable] or {};
4256 Clockwork.SharedTables[sharedTable][key] = value;
4257
4258 if (SERVER) then
4259 Clockwork.datastream:Start(nil, "SetSharedTableVar", {sharedTable = sharedTable, key = key, value = value});
4260 end;
4261 end;
4262end;
4263
4264-- A function to get the shared vars.
4265function Clockwork.kernel:GetSharedVars()
4266 return Clockwork.SharedVars, Clockwork.SharedTables;
4267end;
4268
4269-- A function to get a shared variable.
4270function Clockwork.kernel:GetSharedVar(key, sharedTable)
4271 if (!sharedTable) then
4272 local sharedVars = self:GetSharedVars():Global();
4273 if (sharedVars and sharedVars[key]) then
4274 local class = self:ConvertNetworkedClass(sharedVars[key].class);
4275
4276 if (class) then
4277 return _G["GetGlobal"..class](key);
4278 end;
4279 end;
4280 else
4281 sharedTable = Clockwork.SharedTables[sharedTable];
4282
4283 if (sharedTable) then
4284 return sharedTable[key];
4285 end;
4286 end;
4287end;
4288
4289-- A function to create fake damage info.
4290function Clockwork.kernel:FakeDamageInfo(damage, inflictor, attacker, position, damageType, damageForce)
4291 local damageInfo = DamageInfo();
4292 local realDamage = mathCeil(mathMax(damage, 0));
4293
4294 damageInfo:SetDamagePosition(position);
4295 damageInfo:SetDamageForce(Vector() * damageForce);
4296 damageInfo:SetDamageType(damageType);
4297 damageInfo:SetInflictor(inflictor);
4298 damageInfo:SetAttacker(attacker);
4299 damageInfo:SetDamage(realDamage);
4300
4301 return damageInfo;
4302end;
4303
4304-- A function to unpack a color.
4305function Clockwork.kernel:UnpackColor(color)
4306 return color.r, color.g, color.b, color.a;
4307end;
4308
4309-- A function to parse data in text.
4310function Clockwork.kernel:ParseData(text)
4311 local classes = {"%^", "%!"};
4312
4313 for k, v in pairs(classes) do
4314 for key in stringGmatch(text, v.."(.-)"..v) do
4315 local lower = false;
4316 local amount;
4317
4318 if (stringSub(key, 1, 1) == "(" and stringSub(key, -1) == ")") then
4319 lower = true;
4320 amount = tonumber(stringSub(key, 2, -2));
4321 else
4322 amount = tonumber(key);
4323 end;
4324
4325 if (amount) then
4326 text = stringGsub(text, v..stringGsub(key, "([%(%)])", "%%%1")..v, tostring(self:FormatCash(amount, k == 2, lower)));
4327 end;
4328 end;
4329 end;
4330
4331 for k in stringGmatch(text, "%*(.-)%*") do
4332 k = stringGsub(k, "[%(%)]", "");
4333
4334 if (k != "") then
4335 text = stringGsub(text, "%*%("..k.."%)%*", tostring(Clockwork.option:GetKey(k, true)));
4336 text = stringGsub(text, "%*"..k.."%*", tostring(Clockwork.option:GetKey(k)));
4337 end;
4338 end;
4339
4340 if (CLIENT) then
4341 for k in stringGmatch(text, ":(.-):") do
4342 if (k != "" and input.LookupBinding(k)) then
4343 text = self:Replace(text, ":"..k..":", "<"..stringUpper(tostring(input.LookupBinding(k)))..">");
4344 end;
4345 end;
4346 end;
4347
4348 return Clockwork.config:Parse(text);
4349end;