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