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