DayZ 1.24
Loading...
Searching...
No Matches
ComponentEnergyManager.c
Go to the documentation of this file.
1//-----------------------------
2// ENERGY MANAGER
3//-----------------------------
4/*
5 Author: Boris Vacula
6
7 Documentation can be found at DayZ Confluence >> Camping & Squatting >> Electricity >> Energy Manager functionalities
8
9 This system controls storage, spending and sharing of energy between instances.
10
11 Every EntityAI object which uses this API gains these functions:
12 -It can store some amout of energy
13 -It can use this amount of energy for any kind of functionality
14 -It can share this energy with other devices plugged into it
15 -It will have an ON/OFF switch
16*/
17
19{
20 protected const float DEFAULT_UPDATE_INTERVAL = 15;
21 protected static bool m_DebugPlugs = false; //true; // Use this to toggle visualisation of plug connections
23
24 protected bool m_IsSwichedOn;
25 protected bool m_IsSwichedOnPreviousState; // Necesarry due to synchronization of m_IsSwichedOn
26 protected bool m_IsPassiveDevice;
27 protected bool m_IsWorking;
28 protected bool m_CanWork;
29 protected bool m_CanStopWork;
30 protected bool m_RestorePlugState; // After server restart, this value reports if this device was plugged into something or not at the end of last session.
31 protected bool m_AutoSwitchOff;
33 protected bool m_HasElectricityIcon; // Electricity icon over the item in inventory
35 protected bool m_IsPlugged; // Synchronized variable
37
38 protected int m_MySocketID = -1;
39 protected int m_PlugType;
40 protected int m_EnergySourceStorageIDb1; // Storage persistence ID
41 protected int m_EnergySourceStorageIDb2; // Storage persistence ID
42 protected int m_EnergySourceStorageIDb3; // Storage persistence ID
43 protected int m_EnergySourceStorageIDb4; // Storage persistence ID
45 protected int m_EnergySourceNetworkIDLow = -1; // Network ID
46 protected int m_EnergySourceNetworkIDHigh = -1; // Network ID
47
48 protected float m_EnergyUsage;
49 protected float m_Energy;
50 protected float m_EnergyAtSpawn;
51 protected float m_EnergyStorageMax;
53 protected float m_SocketsCount;
54 protected float m_CordLength;
55 protected float m_LastUpdateTime;
56 protected float m_WetnessExposure;
57 protected float m_UpdateInterval; // Interval of OnWork(...) calls and device updates.
58
59 protected string m_CordTextureFile;
60
61 // Concatenated strings for p3d selections
62 protected static const string SOCKET_ = "socket_";
63 protected static const string _PLUGGED = "_plugged";
64 protected static const string _AVAILABLE = "_available";
65 static const string SEL_CORD_PLUGGED = "cord_plugged";
66 static const string SEL_CORD_FOLDED = "cord_folded";
67
69 EntityAI m_EnergySource; // Energy source can be any EntityAI object
72
76
77 const int MAX_SOCKETS_COUNT = 4;
79
80
81 // Constructor
83 {
84 // Disable debug arrows on public release, so that they don't use their timers.
85#ifndef DEVELOPER
86 m_DebugPlugs = false;
87#endif
88 }
89
91 {
93 {
94 m_DebugPlugArrow.Destroy();
96 }
97 }
98
99 // Initialization. Energy Manager is ready.
100 override void Event_OnInit()
101 {
102 m_ThisEntityAI.m_EM = this;
103 GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnInitEnergy", NULL, 0);
104 }
105
106 // Update debug arrows
108 {
109 if (GetDebugPlugs())
110 {
111 if (GetGame().IsMultiplayer() && GetGame().IsServer())
112 {
113 if (m_DebugUpdate)
114 m_DebugUpdate.Stop();
115
116 return;
117 }
118
120 {
121 m_DebugPlugArrow.Destroy();
123 }
124
125 if (GetEnergySource())
126 {
127 vector from = GetEnergySource().GetPosition() + "0 0.1 0";
128 vector to = m_ThisEntityAI.GetPosition() + "0 0.1 0";
129
130 //No need to draw an arrow in this situation as it would not be visible
131 if (vector.DistanceSq(from, to) == 0)
132 return;
133
134 if (m_ThisEntityAI.GetType() == "BarbedWire") // Special case for debugging of electric fences. Temporal code until offsets in fences are fixed.
135 {
136 EntityAI BBB = m_ThisEntityAI.GetHierarchyParent();
137
138 if (BBB && BBB.GetType() == "Fence")
139 to = to + "0 -1.3 0";
140 }
141
143 }
144 }
145 }
146
147 Shape DrawArrow(vector from, vector to, float size = 0.5, int color = 0xFFFFFFFF, float flags = 0)
148 {
149 vector dir = to - from;
150 dir.Normalize();
151 vector dir1 = dir * size;
152 size = size * 0.5;
153
154 vector dir2 = dir.Perpend() * size;
155
156 vector pts[5];
157 pts[0] = from;
158 pts[1] = to;
159 pts[2] = to - dir1 - dir2;
160 pts[3] = to - dir1 + dir2;
161 pts[4] = to;
162
163 return Shape.CreateLines(color, flags, pts, 5);
164 }
165
167 {
168 return m_ThisEntityAI;
169 }
170
171 // Prepare everything
172 override void Event_OnAwake()
173 {
174 string cfg_item = "CfgVehicles " + m_ThisEntityAI.GetType();
175 string cfg_energy_manager = cfg_item + " EnergyManager ";
176
177 // Read all config parameters
178 m_EnergyUsage = GetGame().ConfigGetFloat(cfg_energy_manager + "energyUsagePerSecond");
179 bool switch_on = GetGame().ConfigGetFloat(cfg_energy_manager + "switchOnAtSpawn");
180 m_AutoSwitchOff = GetGame().ConfigGetFloat(cfg_energy_manager + "autoSwitchOff");
181 m_HasElectricityIcon = GetGame().ConfigGetFloat(cfg_energy_manager + "hasIcon");
182 m_AutoSwitchOffWhenInCargo = GetGame().ConfigGetFloat(cfg_energy_manager + "autoSwitchOffWhenInCargo");
183
184 m_EnergyAtSpawn = GetGame().ConfigGetFloat(cfg_energy_manager + "energyAtSpawn");
186 m_EnergyStorageMax = GetGame().ConfigGetFloat(cfg_energy_manager + "energyStorageMax");
187 m_ReduceMaxEnergyByDamageCoef = GetGame().ConfigGetFloat(cfg_energy_manager + "reduceMaxEnergyByDamageCoef");
188 m_SocketsCount = GetGame().ConfigGetFloat(cfg_energy_manager + "powerSocketsCount");
189
190 m_IsPassiveDevice = GetGame().ConfigGetFloat(cfg_energy_manager + "isPassiveDevice");
191 m_CordLength = GetGame().ConfigGetFloat(cfg_energy_manager + "cordLength");
192 m_PlugType = GetGame().ConfigGetFloat(cfg_energy_manager + "plugType");
193
194 m_AttachmentActionType = GetGame().ConfigGetFloat(cfg_energy_manager + "attachmentAction");
195 m_WetnessExposure = GetGame().ConfigGetFloat(cfg_energy_manager + "wetnessExposure");
196
197 float update_interval = GetGame().ConfigGetFloat(cfg_energy_manager + "updateInterval");
198
199 m_ConvertEnergyToQuantity = GetGame().ConfigGetFloat(cfg_energy_manager + "convertEnergyToQuantity");
200
201
202 // Check if energy->quantity converion is configured properly
203 float cfg_max_quantity = GetGame().ConfigGetFloat(cfg_item + " varQuantityMax");
204
206 {
207 string error = "Error! Item " + m_ThisEntityAI.GetType() + " has invalid configuration of the energy->quantity conversion feature. To fix this, add 'varQuantityMax' parameter with value higher than 0 to the item's config. Then make sure to re-build the PBO containing this item!";
208 Error(error);
210 }
211 else
212 {
214 {
217
218 m_UpdateQuantityTimer.Run(0.3, this, "OnEnergyAdded", NULL, false);
219 }
220 }
221
222 // Set update interval
223 if (update_interval <= 0)
225
227
228 // If energyAtSpawn is present, then use its value for energyStorageMax if that cfg param is not present (for convenience's sake)
229 string cfg_check_energy_limit = cfg_energy_manager + "energyStorageMax";
230
231 if (!GetGame().ConfigIsExisting(cfg_check_energy_limit) && m_Energy > 0)
233
234 // Fill m_CompatiblePlugTypes
235 string cfg_check_plug_types = cfg_energy_manager + "compatiblePlugTypes";
236
237 if (GetGame().ConfigIsExisting(cfg_check_plug_types))
238 {
241 }
242
243 if (GetSocketsCount() > 0)
245
246 if (m_CordLength < 0)
247 {
248 m_CordLength = 0;
249 string error_message_cord = "Warning! " + m_ThisEntityAI.GetType() + ": config parameter 'cordLength' is less than 0! Cord length should not be negative!";
251 }
252
253 if (GetSocketsCount() > 0)
254 {
256 // Prepare the m_DeviceByPlugSelection
257 string cfg_animation_sources = "cfgVehicles " + m_ThisEntityAI.GetType() + " " + "AnimationSources ";
258 int animation_sources_count = GetGame().ConfigGetChildrenCount(cfg_animation_sources);
259
261 {
262 // TO DO: This could be optimized so not all selections on item are considered as plug/socket selections.
263 string selection;
264 GetGame().ConfigGetChildName(cfg_animation_sources, i_selection, selection);
265 m_DeviceByPlugSelection.Set(selection, NULL);
266 }
267 }
268
269
270 // Prepare sockets
272 {
274 string error_message_sockets = "Error! " + m_ThisEntityAI.GetType() + ": config parameter 'powerSocketsCount' is higher than the current limit (" + MAX_SOCKETS_COUNT.ToString() + ")! Raise the limit (constant MAX_SOCKETS_COUNT) or decrease the powerSocketsCount parameter for this device!";
276 }
277
278 m_Sockets[MAX_SOCKETS_COUNT]; // Handles selections for plugs in the sockets. Feel free to change the limit if needed.
279
280 GetGame().ConfigGetText(cfg_energy_manager + "cordTextureFile", m_CordTextureFile);
281
282 if (switch_on)
283 SwitchOn();
284
285 for (int i = 0; i <= GetSocketsCount(); ++i)
286 m_ThisEntityAI.HideSelection(SOCKET_ + i.ToString() + _PLUGGED);
287
288 // Show/hide inventory sockets
290 if (GetSocketsCount() > 0 && IsPlugCompatible(PLUG_COMMON_APPLIANCE) && m_ThisEntityAI.GetType() != "MetalWire") // metal wire filter is hopefully temporal.
292
294
295 m_ThisEntityAI.HideSelection(SEL_CORD_PLUGGED);
296
297
298#ifdef DIAG_DEVELOPER
299 GetGame().m_EnergyManagerArray.Insert(this);
300#endif
301 }
302
303 // Returns the type of this component
304 override int GetCompType()
305 {
307 }
308
309 // When the object is deleted
311 {
312 bool was_working = m_ThisEntityAI.GetCompEM().IsWorking();
313
314 SwitchOff();
316 UnplugThis();
317 SetPowered(false);
318
319 if (was_working)
320 m_ThisEntityAI.OnWorkStop();
321 ;
322 }
323
324 //Restart the debug timer when relogging
326 {
327 if (m_DebugPlugs)
328 {
329 if (!m_DebugUpdate)
331
332 if (!m_DebugUpdate.IsRunning())
333 m_DebugUpdate.Run(0.01, this, "DebugUpdate", NULL, true);
334 }
335 else
336 {
338 {
339 m_DebugPlugArrow.Destroy();
341 }
342 }
343 }
344
346 {
347 return m_DebugPlugs;
348 }
349
351 {
353 RefreshDebug();
354 }
355
356 //======================================================================================
357 // PUBLIC FUNCTIONS
358 // Use these to control the Energy Manager
359 // Functions are in order of their return value: void, bool, int, float, string, array.
360 //======================================================================================
361
363 void SwitchOn()
364 {
366
367 if (GetGame().IsServer() || !GetGame().IsMultiplayer())
368 {
369 if (CanSwitchOn())
370 {
371 m_IsSwichedOn = true;
372 Synch();
373
374 DeviceUpdate(); // 'Wake up' this device now
375 StartUpdates();
376
377 // 'Wakes up' all connected devices
379
381
382 // Call event
383 GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnSwitchOn", NULL, 0);
384 }
385 }
386
387 if (!GetGame().IsServer() && GetGame().IsMultiplayer()/* && CanSwitchOn() */) // I want the CanSwitchOn() check, but when it's here, the OnSwitchOn() event is never called on client-side due to engine's synchronization system changing the m_IsSwichedOn to true without any specific event beign called. (Yes there is OnVariablesSynchronized() but that is called also when m_CanWork is synchronized, so I need to write a method of knowing when was this specific value changed.)
388 GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnSwitchOn", NULL, 0);
389 }
390
393 {
395
396 if (GetGame().IsServer() || !GetGame().IsMultiplayer())
397 {
398 if (CanSwitchOff())
399 {
400 m_IsSwichedOn = false;
401 Synch();
402
403 if (IsWorking())
404 {
405 StopUpdates();
406 DeviceUpdate();
407 }
408
409 // 'Wakes up' all connected devices
411
413
414 // Call event
415 GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnSwitchOff", NULL, 0);
416 }
417 }
418
419 if (!GetGame().IsServer() && GetGame().IsMultiplayer())
420 {
421 m_IsSwichedOn = false;
422 GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnSwitchOff", NULL, 0);
423 }
424 }
425
427 void SetPassiveState(bool state = true)
428 {
431 DeviceUpdate();
432 }
433
436 {
437 if (GetGame())
438 {
440 bool deviceFound = false;
441
442 for (int i = indexStart; i >= 0; --i)
443 {
445
447 {
448 GetPluggedDevices().Remove(i);
449 deviceFound = true;
450 break;
451 }
452 }
453
454 if (deviceFound)
455 {
456 int socket_ID = device_to_unplug.GetCompEM().GetMySocketID();
458 device_to_unplug.GetCompEM().SetEnergySource(null);
459 device_to_unplug.GetCompEM().DeviceUpdate();
460 device_to_unplug.GetCompEM().StartUpdates();
461 device_to_unplug.GetCompEM().WakeUpWholeBranch(m_ThisEntityAI);
462
464 {
465 m_DebugPlugArrow.Destroy();
467 }
468
470 device_to_unplug.GetCompEM().OnIsUnplugged(m_ThisEntityAI);
471 device_to_unplug.ShowSelection(SEL_CORD_FOLDED);
472 device_to_unplug.HideSelection(SEL_CORD_PLUGGED);
473 }
474 }
475 }
476
479 {
480 if (GetGame())
481 {
482 if (GetEnergySource())
483 GetEnergySource().GetCompEM().UnplugDevice(m_ThisEntityAI);
484 }
485 }
486
489 {
490 if (GetPluggedDevices()) // This check is necesarry in case this function is called before initialization
491 {
493 for (int i = indexStart; i >= 0; --i)
495 }
496 }
497
498 // Used only for storing of the plug's state through server restart
500 {
502 }
503
506 {
507 if (GetGame().IsServer() || !GetGame().IsMultiplayer()) // Client can't change energy value.
508 {
509 m_ThisEntityAI.SetWeightDirty();
510 float old_energy = m_Energy;
512
515 }
516 }
517
520 {
522 }
523
526 {
527 // Lets update sockets, if there are any
528 int slots_c = GetSocketsCount();
529
530 for (int i = 0; i < slots_c; ++i)
531 {
533
534 if (plug_owner)
535 {
536 string plugged_selection = SOCKET_ + (i + 1).ToString() + _PLUGGED;
537 string available_selection = SOCKET_ + (i + 1).ToString() + _AVAILABLE;
538 m_ThisEntityAI.ShowSelection(plugged_selection);
539 m_ThisEntityAI.HideSelection(available_selection);
540 string texture_path = plug_owner.GetCompEM().GetCordTextureFile();
541 int selection_index = m_ThisEntityAI.GetHiddenSelectionIndex(plugged_selection);
543 }
544 else
545 {
546 m_ThisEntityAI.ShowSelection(SOCKET_ + (i + 1).ToString() + _AVAILABLE);
547 m_ThisEntityAI.HideSelection(SOCKET_ + (i + 1).ToString() + _PLUGGED);
548 }
549 }
550
551 // Now lets update the cord/plug state
552 if (GetEnergySource())
553 {
554 m_ThisEntityAI.ShowSelection(SEL_CORD_PLUGGED);
555 m_ThisEntityAI.HideSelection(SEL_CORD_FOLDED);
556 }
557 else
558 {
559 m_ThisEntityAI.ShowSelection(SEL_CORD_FOLDED);
560 m_ThisEntityAI.HideSelection(SEL_CORD_PLUGGED);
561 }
562 }
563
566 {
567 if (m_ThisEntityAI.GetCompEM().GetEnergySource())
568 {
569 EntityAI player = m_ThisEntityAI.GetHierarchyRootPlayer();
570 // Check if the item is held in hands during advanced placement
571 if (player)
572 {
573 // Measure distance from the player
574 vector playerPosition = player.GetPosition();
576 UnplugThis();
577 }
578 else
579 {
580 // Measure distance from the device
581 vector itemPosition = m_ThisEntityAI.GetPosition();
582
583 if (m_ThisEntityAI.GetHierarchyParent())
584 itemPosition = m_ThisEntityAI.GetHierarchyParent().GetPosition();
585
587 UnplugThis();
588 }
589 }
590 }
591
592 // Returns an array of plug types this device can accept
597
598 // Stores IDs of the energy source.
606
612
615 {
617 }
618
619 // Sets the plug type (for plug -> socket compatibility checks).
621 {
623 }
624
625 // Sets the new attachment action type.
630
633 {
635 }
636
639 {
640 string cfg_energy_usage = "CfgVehicles " + m_ThisEntityAI.GetType() + " EnergyManager ";
641 m_EnergyUsage = GetGame().ConfigGetFloat(cfg_energy_usage + "energyUsagePerSecond");
642 }
643
644 // Sets path to the cord texture file.
646 {
648 }
649
650 // Sets energy source. Intended to be called only on client through RPC.
655
661
662
668
669 // Checks whenever this device can work or not and updates this information on all clients. Can be called many times per frame because synchronization happens only once if a change has occured.
671 {
672 if (GetGame().IsServer() || !GetGame().IsMultiplayer())
673 {
674 bool current_state = CanWork();
675
677 {
679 Synch();
680
681 if (m_ThisEntityAI && m_ThisEntityAI.GetHierarchyParent() && m_ThisEntityAI.GetHierarchyParent().GetCompEM())
682 m_ThisEntityAI.GetHierarchyParent().GetCompEM().UpdateCanWork();
683 }
684 }
685 }
686
688 {
690 {
691 if (IsSwitchedOn())
692 SwitchOff();
693 }
694 }
695
698 {
700 }
701
702 // Returns true if this device was plugged into something at the end of previous session
704 {
705 return m_RestorePlugState;
706 }
707
710 {
711 return energy_source.GetCompEM().PlugInDevice(m_ThisEntityAI, socket_id);
712 }
713
716 {
717 if (!IsSwitchedOn())
718 return true;
719
720 return false;
721 }
722
723
729 bool CanWork(float test_energy = -1)
730 {
731 if (GetGame().IsMultiplayer() && GetGame().IsClient())
732 return m_CanWork;
733
734 if (m_ThisEntityAI && m_ThisEntityAI.IsRuined())
735 return false;
736
737 // Check if the power source(s) (which can be serially connected) can provide needed energy.
739 float gathered_energy = GetEnergy();
741
742 if (energy_usage == -1)
744
745 if (!CheckWetness())
746 return false;
747
748 if (gathered_energy <= 0 && energy_usage <= 0) //empty power source
749 return false;
750
751 int cycle_limit = 500; // Sanity check to definitely avoid infinite cycles
752
753 while (gathered_energy < energy_usage) // Look for energy source if we don't have enough stored energy
754 {
755 // Safetycheck!
756 if (cycle_limit > 0)
757 cycle_limit--;
758 else
759 {
760 DPrint("Energy Manager ERROR: The 'cycle_limit' safety break had to be activated to prevent possible game freeze. Dumping debug information...");
761 //Print(m_ThisEntityAI);
762 //Print(this);
763 //Print(energy_source);
764
765 if (energy_source.GetCompEM())
766 {
767 //Print(energy_source.GetCompEM());
768 }
769
770 //Print(gathered_energy);
771 //Print(energy_usage);
772
773 //Print(m_ThisEntityAI.GetPosition());
774
775 if (energy_source)
776 {
777 //Print(energy_source.GetPosition());
778 }
779
780 //Print("End of the 'cycle_limit' safety break ^ ");
781
782 return false;
783 }
784 // ^ Safetycheck!
785
786 if (energy_source && energy_source != m_ThisEntityAI && !energy_source.IsRuined() && energy_source.GetCompEM() && energy_source.GetCompEM().IsSwitchedOn() && energy_source.GetCompEM().CheckWetness())
787 {
788 gathered_energy = gathered_energy + energy_source.GetCompEM().GetEnergy();
789 energy_source = energy_source.GetCompEM().GetEnergySource();
790 }
791 else
792 {
793 // No power source, no energy.
794 return false;
795 }
796 }
797
798 // Enough energy was found
799 return true;
800 }
801
804 {
805 return (m_ThisEntityAI.GetWet() <= 1 - m_WetnessExposure);
806 }
807
810 {
811 if (IsPassive())
812 return false;
813
814 return IsSwitchedOn();
815 }
816
817 // Returns previous state of the switch.
822
825 {
826 return m_IsSwichedOn;
827 }
828
831 {
832 if (IsPlugged())
833 return false;
834
835 return true;
836 }
837
840 {
841 return m_IsPassiveDevice;
842 }
843
846 {
847 return m_IsPlugged;
848 }
849
850
852 bool ConsumeEnergy(float amount)
853 {
854 return FindAndConsumeEnergy(m_ThisEntityAI, amount, true);
855 }
856
859 {
860 return m_IsWorking;
861 }
862
865 {
866 if (GetEnergy() > GetEnergyUsage())
867 return true;
868
869 return false;
870 }
871
874 {
875 if (socket_id == -1)
876 {
879
881 return true;
882
883 return false;
884 }
885 else
886 {
888
889 if (device)
890 return false;
891 else
892 return true;
893 }
894 }
895
898 {
899 if (plug_ID == PLUG_UNDEFINED)
900 {
901 return true; // When plugType is undefined in config then make it compatible.
902 }
903
905 {
906 for (int i = 0; i < m_CompatiblePlugTypes.Count(); i++)
907 {
909
911 return true;
912 }
913 }
914 else
915 {
916 // Since the config parameter compatiblePlugTypes is not present, then accept all plugs for simplicity's sake
917 return true;
918 }
919
920 return false;
921 }
922
925 {
926 // The following conditions are broken down for the sake of easier reading/debugging.
927
929 {
930 if (device_to_plug.GetCompEM().GetEnergySource() != m_ThisEntityAI)
931 {
932 if (IsPlugCompatible(device_to_plug.GetCompEM().GetPlugType()))
933 {
934 if (device_to_plug.GetCompEM().IsEnergySourceAtReach(device_to_plug.GetPosition(), 0, m_ThisEntityAI.GetPosition()))
935 return true;
936 }
937 }
938 }
939
940 return false;
941 }
942
945 {
946 return potential_energy_provider.GetCompEM().CanReceivePlugFrom(m_ThisEntityAI);
947 }
948
951 {
953 }
954
960
978 {
979 if (!IsPlugged() && override_source_position == "-1 -1 -1")
980 return false;
981
982 if (GetCordLength() == 0) // 0 is an exception, which means infinitely long cable.
983 return true;
984
986 float distance;
987
988 if (override_source_position == "-1 -1 -1")
989 {
991
992 if (!energy_source)
993 return false;
994
995 source_pos = energy_source.GetPosition();
997 }
998 else
999 {
1002 }
1003
1005 return false;
1006 else
1007 return true;
1008 }
1009
1014
1017 {
1018 if (GetPluggedDevices())
1019 {
1021
1022 for (int i = socket_count; i >= 0; --i)
1023 {
1024 string real_selection = SOCKET_ + i.ToString() + _PLUGGED;
1025
1027 return true;
1028 }
1029 }
1030
1031 return false;
1032 }
1033
1034
1037 {
1038 return m_SocketsCount;
1039 }
1040
1043 {
1044 return m_PlugType;
1045 }
1046
1047 // Returns the action ID which is supposed to be done upon receiving an attachment
1049 {
1051 }
1052
1053 // Returns persistent ID (block 1) of the energy source
1058
1059 // Returns persistent ID (block 2) of the energy source
1064
1065 // Returns persistent ID (block 3) of the energy source
1070
1071 // Returns persistent ID (block 4) of the energy source
1076
1077 // Returns network ID (low) of the energy source
1082
1083 // Returns network ID (high) of the energy source
1088
1091 {
1092 if (GetPluggedDevices())
1093 return GetPluggedDevices().Count();
1094
1095 return 0;
1096 }
1097
1100 {
1101 if (m_EnergyStorageMax > 0)
1102 {
1103 int coef = Math.Round(m_Energy / m_EnergyStorageMax * 100);
1104 return coef;
1105 }
1106
1107 return 0;
1108 }
1109
1112 {
1113 if (m_EnergyStorageMax > 0)
1115
1116 return 0;
1117 }
1118
1121 {
1122#ifdef DIAG_DEVELOPER
1123 if (FeatureTimeAccel.GetFeatureTimeAccelEnabled(ETimeAccelCategories.ENERGY_CONSUMPTION) || (FeatureTimeAccel.GetFeatureTimeAccelEnabled(ETimeAccelCategories.ENERGY_RECHARGE)))
1124 {
1125 return 1;//when modifying time accel, we might want to see things happen when they should, instead of waiting for the next tick
1126 }
1127#endif
1128 return m_UpdateInterval;
1129 }
1130
1133 {
1134 return m_WetnessExposure;
1135 }
1136
1139 {
1140 return m_EnergyUsage;
1141 }
1142
1145 {
1146 return m_Energy;
1147 }
1148
1151 {
1152 if (added_energy != 0)
1153 {
1154 //Print("AddEnergy ---------> " + added_energy + " " + this + " " +m_ThisEntityAI.ClassName());
1155#ifdef DIAG_DEVELOPER
1156 if (FeatureTimeAccel.GetFeatureTimeAccelEnabled(ETimeAccelCategories.ENERGY_CONSUMPTION) && added_energy < 0)
1157 {
1158 float timeAccel = FeatureTimeAccel.GetFeatureTimeAccelValue();
1160 }
1161#endif
1162
1163 bool energy_was_added = (added_energy > 0);
1164
1168 StartUpdates();
1169
1170 if (energy_was_added)
1171 OnEnergyAdded();
1172 else
1174
1176 }
1177
1178 return 0;
1179 }
1180
1183 {
1184 float max_health = 0;
1185
1186 if (m_ThisEntityAI.HasDamageSystem())
1187 max_health = m_ThisEntityAI.GetMaxHealth("", "");
1188 //else if ( m_ReduceMaxEnergyByDamageCoef != 0 )
1189 // Error("[ERROR] ReduceMaxEnergyByDamageCoef is setup but " + m_ThisEntityAI.GetType() + " does not have a Damage System");
1190
1192 return GetEnergyMaxPristine();
1193
1194 float health = 100;
1195
1196 if (GetGame().IsServer() || !GetGame().IsMultiplayer()) // TO DO: Remove this IF when method GetHealth can be called on client!
1197 health = m_ThisEntityAI.GetHealth("", "");
1198
1199 float damage_coef = 1 - (health / max_health);
1200
1202 }
1203
1206 {
1207 return m_EnergyStorageMax;
1208 }
1209
1211 {
1212 return m_EnergyAtSpawn;
1213 }
1214
1217 {
1218 return m_CordLength;
1219 }
1220
1223 {
1224 return m_EnergySource;
1225 }
1226
1229 {
1230 return m_Sockets[id];
1231 }
1232
1241
1244 {
1245 if (GetPluggedDevicesCount() > 0)
1246 return GetPluggedDevices().Get(0);
1247
1248 return NULL;
1249 }
1250
1253 {
1254 return m_CordTextureFile;
1255 }
1256
1262
1265 {
1268 for (int i = 0; i < plugged_devices_c; ++i)
1269 {
1271 if (IsSwitchedOn())
1272 return_array.Insert(device);
1273 }
1274
1275 return return_array;
1276 }
1277
1278
1279 /*===================================
1280 PUBLIC EVENTS
1281 ===================================*/
1282
1283 // Called every device update if its supposed to do some work. The update can be every second or at random, depending on its manipulation.
1285 {
1287 }
1288
1289 // Called when this device is plugged into some energy source
1291 {
1292 if (m_DebugPlugs)
1293 {
1294 if (!m_DebugUpdate)
1296
1297 if (!m_DebugUpdate.IsRunning())
1298 m_DebugUpdate.Run(0.01, this, "DebugUpdate", NULL, true);
1299 }
1300
1301 UpdateCanWork();
1302 m_ThisEntityAI.OnIsPlugged(source_device);
1303 }
1304
1305 // Called when this device is UNPLUGGED from the energy source
1311
1312 // When something is plugged into this device
1314 {
1315 //play sound
1316 if (device.GetCompEM().GetPlugType() == PLUG_COMMON_APPLIANCE && m_ThisEntityAI.IsInitialized())
1317 {
1319 m_ThisEntityAI.PlaySoundSet(sound_plug, "cablereel_plugin_SoundSet", 0, 0);
1320 }
1321
1322 m_ThisEntityAI.OnOwnSocketTaken(device);
1323 }
1324
1325 // When something is UNPLUGGED from this device
1327 {
1328 //play sound
1329 if (device.GetCompEM().GetPlugType() == PLUG_COMMON_APPLIANCE && m_ThisEntityAI.IsInitialized())
1330 {
1332 m_ThisEntityAI.PlaySoundSet(sound_unplug, "cablereel_unplug_SoundSet", 0, 0);
1333 }
1334
1335 m_ThisEntityAI.OnOwnSocketReleased(device);
1336 }
1337
1338
1339 // Handles automatic attachment action
1341 {
1343
1345 {
1346 if (elec_device.GetCompEM().CanReceivePlugFrom(m_ThisEntityAI))
1348 }
1350 elec_device.GetCompEM().PlugThisInto(m_ThisEntityAI);
1351 }
1352
1353 // Handles automatic detachment action
1355 {
1357
1359 {
1361 UnplugThis();
1362 }
1364 elec_device.GetCompEM().UnplugThis();
1365 }
1366
1367 // Starts the device's main cycle
1369 {
1370 if (!m_IsPassiveDevice)
1371 {
1372 if (!m_UpdateTimer)
1374
1375 if (!m_UpdateTimer.IsRunning()) // Makes sure the timer is NOT running already
1376 m_UpdateTimer.Run(GetUpdateInterval(), this, "DeviceUpdate", null, true);
1377 }
1378 }
1379
1382 {
1383 m_ThisEntityAI.OnEnergyConsumed();
1384 }
1385
1388 {
1390 {
1391 m_UpdateQuantityTimer.Stop();
1393 }
1394
1395 m_ThisEntityAI.OnEnergyAdded();
1396 }
1397
1398
1399 /*===================================
1400 PROTECTED FUNCTIONS
1401 ===================================*/
1402
1403 // Stops the device's main cycle
1404 protected void StopUpdates()
1405 {
1406 if (m_UpdateTimer)
1407 {
1408 m_UpdateTimer.Stop();
1409 m_UpdateTimer = NULL; // Delete timer from memory
1410 }
1411 }
1412
1417 {
1419 if (GetSocketsCount() > 0)
1420 {
1422
1423 foreach (EntityAI device : devices)
1424 {
1425 if (device != originalCaller) // originalCaller check here prevents infinite loops
1426 device.GetCompEM().InteractBranch(originalCaller, player, system);
1427 }
1428 }
1429 }
1430
1433 {
1434 m_ThisEntityAI.IncreaseLifetime();
1435
1436 }
1437
1438 // 'Wakes up' all devices down the network so they start working, if they have enough power, and are switched ON
1440 {
1441 if (GetSocketsCount() > 0)
1442 {
1444 int plugged_devices_c = plugged_devices.Count();
1445
1446 for (int i = 0; i < plugged_devices_c; ++i)
1447 {
1449 if (device != original_caller) // original_caller check here prevents infinite loops
1450 {
1451 device.GetCompEM().UpdateCanWork();
1452 device.GetCompEM().DeviceUpdate();
1453 device.GetCompEM().StartUpdates();
1454 device.GetCompEM().WakeUpWholeBranch(original_caller);
1455 }
1456 }
1457 }
1458 }
1459
1460 // Finds an available socket and plugs the given device into it.
1461 // This is mainly about visualisation.
1463 {
1464 if (socket_id >= 0)
1465 {
1467
1469 {
1471 return;
1472 }
1473 }
1474
1475 int slots_c = GetSocketsCount();
1476
1477 for (int i = 0; i < slots_c; ++i)
1478 {
1480
1481 if (!plug_owner) // Check if this socket is available
1482 {
1484 break;
1485 }
1486 }
1487 }
1488
1489 // Updates socket selections (plugged/unplugged) of the given ID and sets color texture of the plug.
1491 {
1493
1496 m_ThisEntityAI.ShowSelection(plugged_selection);
1497
1499 m_ThisEntityAI.HideSelection(unplugged_selection);
1500 string texture_path = device_to_plug.GetCompEM().GetCordTextureFile();
1501 int selection_index = m_ThisEntityAI.GetHiddenSelectionIndex(plugged_selection);
1502 m_ThisEntityAI.SetObjectTexture(selection_index, texture_path);
1503 device_to_plug.GetCompEM().SetMySocketID(socket_id);
1504 }
1505
1506
1507 // Sets energy source for this device
1509 {
1511
1512 if (source)
1513 {
1514 m_IsPlugged = true;
1515 StartUpdates();
1516 }
1517 else
1518 {
1519 m_IsPlugged = false;
1522 }
1523
1524 if (m_EnergySource)
1526
1527 Synch();
1528 }
1529
1530 // Plugs the given device into this one
1532 {
1534 {
1535 device_to_plug.IncreaseLifetime();
1537 if (device_to_plug.GetCompEM().IsPlugged())
1538 device_to_plug.GetCompEM().UnplugThis();
1539
1541 device_to_plug.GetCompEM().SetEnergySource(m_ThisEntityAI);
1542
1543 PlugCordIntoSocket(device_to_plug, socket_id); // Visualisation
1545
1546 device_to_plug.GetCompEM().OnIsPlugged(m_ThisEntityAI);
1548
1549 if (GetGame().IsServer() || !GetGame().IsMultiplayer())
1550 {
1551 device_to_plug.HideSelection(SEL_CORD_FOLDED);
1552 device_to_plug.ShowSelection(SEL_CORD_PLUGGED);
1553 }
1554
1555 return true;
1556 }
1557
1558 return false;
1559 }
1560
1561 // Sets the device to which the given plug selection belongs to
1567
1568 // Frees the given socket.
1569 // This is only about visualisation.
1571 {
1573
1574 if (plug_owner)
1575 {
1578 m_ThisEntityAI.ShowSelection(unplugged_selection);
1579
1581 m_ThisEntityAI.HideSelection(plugged_selection);
1583 plug_owner.GetCompEM().SetMySocketID(-1);
1584 }
1585 }
1586
1587 // Sets the state of the device
1588 protected void SetPowered(bool state)
1589 {
1591 }
1592
1593 // Tries to consume the given amount of energy. If there is none in this device, then it tries to take it from some power source.
1594 protected bool FindAndConsumeEnergy(EntityAI original_caller, float amount, bool ignore_switch_state = false)
1595 {
1596 if ((ignore_switch_state || IsSwitchedOn()) && !m_ThisEntityAI.IsRuined())
1597 {
1598 float available_energy = AddEnergy(-amount);
1599
1600 if (available_energy < 0 && IsPlugged())
1601 {
1602 // This devices does not has enough of stored energy, therefore it will take it from its power source (which can be a chain of cable reels)
1604
1605 if (next_power_source && next_power_source != original_caller) // Prevents infinite loop if the power source is the original caller itself
1606 return next_power_source.GetCompEM().FindAndConsumeEnergy(original_caller, -available_energy);
1607 }
1608
1609 if (available_energy >= 0)
1610 return true;
1611
1612 return false;
1613 }
1614 else
1615 return false;
1616 }
1617
1618 // Gets the socket ID this device is powered from.
1619 protected int GetMySocketID()
1620 {
1621 return m_MySocketID;
1622 }
1623
1624 // Sets the socket ID this device is plugged into.
1625 protected void SetMySocketID(int slot_ID)
1626 {
1628 }
1629
1630 void Synch()
1631 {
1632 m_ThisEntityAI.SetSynchDirty();
1633 }
1634
1636 {
1637 m_LastUpdateTime = 0;
1638 }
1639
1644
1646 {
1647 return GetGame().GetTime();
1648 }
1649
1650 // Updates the device's state of power. This function is visualized in the diagram at DayZ Confluence >> Camping & Squatting >> Electricity >> Energy Manager functionalities
1652 {
1653 /*
1654 vector pos = m_ThisEntityAI.GetPosition();
1655 string debug_message = "Object " + m_ThisEntityAI.GetType() + " | Energy: " + GetEnergy() + " | IsAtReach: " + (IsEnergySourceAtReach(pos)).ToString();
1656 Print(debug_message);
1657 */
1658
1659 if (!m_IsPassiveDevice)
1660 {
1661 // 'm_ThisEntityAI' and 'this' must be checked because this method is caled from a timer
1662 if (m_ThisEntityAI && this && IsSwitchedOn() && !m_ThisEntityAI.IsRuined() && CheckWetness() && m_CanWork && !GetGame().IsMissionMainMenu())
1663 {
1664 bool was_powered = IsWorking();
1666 // Make sure to use only as much % of energy as needed since this function can be called at random.
1667
1668 if (m_LastUpdateTime == 0)
1669 {
1672 }
1673 else
1674 {
1677 consumed_energy_coef = time / 1000;
1678 }
1679
1680 if (consumed_energy_coef > 0) // Prevents calling of OnWork events when no energy is consumed
1681 {
1684 bool has_consumed_enough = true;
1685
1686 if (GetGame().IsServer() || !GetGame().IsMultiplayer()) // single player or server side multiplayer
1688
1690
1692 {
1693 if (!was_powered)
1694 {
1695 m_CanStopWork = true;
1697 GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnWorkStart", NULL, 0);
1698 UpdateCanWork();
1699 }
1700
1702 }
1703 else
1704 {
1705 if (was_powered)
1706 {
1707 if (m_CanStopWork)
1708 {
1709 m_CanStopWork = false;
1711 GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnWorkStop", NULL, 0); // This event is called only once when the device STOPS being powered
1712 UpdateCanWork();
1713
1714 if (m_AutoSwitchOff)
1715 SwitchOff();
1716 }
1717 }
1718
1719 StopUpdates();
1720 }
1721 }
1722 else
1724 }
1725 else if (this && m_ThisEntityAI)
1726 {
1727 SetPowered(false);
1728 StopUpdates();
1729
1730 if (m_CanStopWork)
1731 {
1732 m_CanStopWork = false;
1734 GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnWorkStop", NULL, 0); // This event is called only once when the device STOPS being powered
1735 UpdateCanWork();
1736
1737 if (m_AutoSwitchOff)
1738 SwitchOff();
1739 }
1740 }
1741 }
1742 }
1743}
const int PLUG_ATTACHMENTS_INTO_THIS
Definition _constants.c:36
const int PLUG_THIS_INTO_ATTACHMENT
Definition _constants.c:35
const int PLUG_UNDEFINED
Definition _constants.c:23
const int PLUG_COMMON_APPLIANCE
Definition _constants.c:25
proto string ToString()
array< ref PlayerStatBase > Get()
static const string SEL_CORD_FOLDED
void SetElectricityIconVisibility(bool make_visible)
Energy manager: Sets visibility of the electricity icon (bolt).
void SwitchOn()
Energy manager: Switches ON the device so it starts doing its work if it has enough energy.
float GetEnergyMax()
Energy manager: Returns the maximum amount of energy this device can curently store....
void UnplugAllDevices()
Energy manager: Unplugs everything directly connected to this device.
void InteractBranch(EntityAI originalCaller, Man player=null, int system=0)
float GetEnergyUsage()
Energy manager: Returns the number of energy this device needs to run itself (See its config >> energ...
void OnInteractBranch(EntityAI originalCaller, Man player, int system)
Called when the player is interacting with an item containing this energy component,...
bool IsWorking()
Energy manager: Returns true if this device is working right now.
void SetEnergyMaxPristine(float new_limit)
Energy manager: Changes the maximum amount of energy this device can store (when pristine).
Shape DrawArrow(vector from, vector to, float size=0.5, int color=0xFFFFFFFF, float flags=0)
static const string _AVAILABLE
void GetCompatiblePlugTypes(out TIntArray IDs)
void SetEnergy0To1(float energy01)
Energy manager: Sets stored energy for this device between 0 and MAX based on relative input value be...
bool HasFreeSocket(int socket_id=-1)
Energy manager: Returns true if this device has any free socket to receive a plug....
float GetWetnessExposure()
Returns wetness exposure value defined in config.
void WakeUpWholeBranch(EntityAI original_caller)
void UnplugDevice(EntityAI device_to_unplug)
Energy manager: Unplugs the given device from this one.
bool CheckWetness()
Energy manager: Checks if this device is being stopped from working by its wetness level....
void SetUpdateInterval(float value)
Energy manager: Sets the interval of the OnWork(...) calls. Changing this value does not change the r...
void SetPassiveState(bool state=true)
Energy manager: Changes the status of this device. When it's passive (true), the main timer and OnWor...
bool PlugInDevice(EntityAI device_to_plug, int socket_id=-1)
void SetAttachmentAction(int new_action_type)
bool FindAndConsumeEnergy(EntityAI original_caller, float amount, bool ignore_switch_state=false)
void OnEnergyAdded()
Energy manager: Called when energy was added on this device.
void UnplugCordFromSocket(int socket_to_unplug_ID)
float AddEnergy(float added_energy)
Energy manager: Adds energy to this device and clamps it within its min/max storage limits....
void UnplugThis()
Energy manager: Unplugs this device from its power source.
int GetPluggedDevicesCount()
Energy manager: Returns the number of devices plugged into this one.
void SetCordLength(float new_length)
Energy manager: Changes the length of the virtual power cord.
void SetEnergySourceClient(EntityAI source)
void SetEnergySource(EntityAI source)
int GetEnergy0To100()
Energy manager: Returns % of stored energy this device has as integer (from 0 to 100)
void PlugCordIntoSocket(EntityAI device_to_plug, int socket_id=-1)
bool IsEnergySourceAtReach(vector from_position, float add_tolerance=0, vector override_source_position="-1 -1 -1")
Energy manager: Returns true if this device's virtual power cord can reach its energy source at the g...
ref map< string, EntityAI > m_DeviceByPlugSelection
void HandleMoveInsideCargo(EntityAI container)
float GetEnergy()
Energy manager: Returns the amount of stored energy this device has.
int GetPlugType()
Energy manager: Returns plug type. Check \DZ\data\basicDefines.hpp OR \Scripts\Classes\Component_cons...
bool CanBePluggedInto(EntityAI potential_energy_provider)
Energy manager: Returns true if this device can be plugged into the given energy source....
bool IsPlugged()
Energy manager: Returns true if this device is plugged into some other device (even if they are OFF o...
EntityAI GetEnergySource()
Energy manager: Returns the energy source this device is plugged into.
ref array< EntityAI > m_PluggedDevices
void OnAttachmentRemoved(EntityAI elec_device)
void OnWork(float consumed_energy)
array< EntityAI > GetPluggedDevices()
Energy manager: Returns an array of devices which are plugged into this one.
bool IsPassive()
Energy manager: Returns true if this device is set to be passive. False if otherwise.
bool HasConversionOfEnergyToQuantity()
Energy manager: Returns true if this item automatically converts its energy to quantity.
void StoreEnergySourceIDs(int b1, int b2, int b3, int b4)
void UpdatePlugState()
Energy manager: Unplugs this device when it's necesarry.
void OnAttachmentAdded(EntityAI elec_device)
void OnIsPlugged(EntityAI source_device)
EntityAI m_Sockets[MAX_SOCKETS_COUNT]
string GetCordTextureFile()
Energy manager: Returns path to the cord texture file.
bool IsSelectionAPlug(string selection_to_test)
Energy manager: Returns true if this selection is a plug that's plugged into this device....
void SetEnergy(float new_energy)
Energy manager: Sets stored energy for this device. It ignores the min/max limit!
void SetPlugOwner(string selection_name, EntityAI device)
float GetCordLength()
Energy manager: Returns the length of the virtual power cord.
EntityAI GetPlugOwner(string plug_selection_name)
Energy manager: Returns the device to which the given plug selection belongs to.
bool CanWork(float test_energy=-1)
Energy manager: Checks whenever this device can do work or not.
EntityAI GetPluggedDevice()
Energy manager: Returns a device which is plugged into this one. If there are more devices to choose ...
void UpdateSelections()
Energy manager: Shows/Hides all selections this system works with. Call this if something is wrong wi...
void OnIsUnplugged(EntityAI last_energy_source)
static const string SEL_CORD_PLUGGED
bool IsCordFolded()
Energy manager: Returns true if the cord of this device is folded. Returns false if it's plugged.
float GetEnergy0To1()
Energy manager: Returns % of stored energy this device has as float (from 0.0 to 1....
void SwitchOff()
Energy manager: Switches OFF the device.
void OnOwnSocketReleased(EntityAI device)
array< EntityAI > GetPoweredDevices()
Energy manager: Returns an array of devices which are plugged into this one and are turned on.
bool ConsumeEnergy(float amount)
Energy manager: Consumes the given amount of energy. If there is not enough of stored energy in this ...
bool CanSwitchOn()
Energy manager: Checks if the device can be switched ON.
void ResetEnergyUsage()
Energy manager: Resets energy usage to default (config) value.
bool IsPlugCompatible(int plug_ID)
Energy manager: Checks if the given plug is compatible with this device's socket. Used by CanReceiveP...
void SetEnergyUsage(float new_usage)
Energy manager: Changes the energy usage per second.
void OnEnergyConsumed()
Energy manager: Called when energy was consumed on this device.
void SetCordTextureFile(string new_path)
bool CanSwitchOff()
Energy manager: Checks if the device can be switched OFF.
int GetSocketsCount()
Energy manager: Returns the count of power sockets (whenever used or not)
void SetDeviceBySocketID(int id, EntityAI plugged_device)
Energy manager: Stores the device which is plugged into the given socket ID.
void UpdateSocketSelections(int socket_id, EntityAI device_to_plug)
float GetUpdateInterval()
Energy manager: Returns the update interval of this device.
bool HasElectricityIcon()
Energy manager: Returns true if the electricity icon (bolt) is supposed to be visible for this device...
void OnOwnSocketTaken(EntityAI device)
bool PlugThisInto(EntityAI energy_source, int socket_id=-1)
Energy manager: Attempts to plug this device into the energy_source. Returns true if the action was s...
EntityAI GetDeviceBySocketID(int id)
Energy manager: Returns the device which is plugged into the given socket ID.
float GetEnergyMaxPristine()
Energy manager: Returns the maximum amount of energy this device can store. It's damage is NOT taken ...
bool IsSwitchedOn()
Energy manager: Returns state of the switch. Whenever the device is working or not does not matter....
bool HasEnoughStoredEnergy()
Energy manager: Returns true if this device has enough of stored energy for its own use.
bool CanReceivePlugFrom(EntityAI device_to_plug)
Energy manager: Returns true if this device can receive power plug of the other device.
EntityAI m_ThisEntityAI
Definition Component.c:24
Wrapper class for managing sound through SEffectManager.
Definition EffectSound.c:5
Definition EnMath.c:7
Result for an object found in CGame.IsBoxCollidingGeometryProxy.
static proto native float DistanceSq(vector v1, vector v2)
Returns the square distance between tips of two 3D vectors.
proto float Normalize()
Normalizes vector. Returns length.
static proto native float Distance(vector v1, vector v2)
Returns the distance between tips of two 3D vectors.
vector Perpend()
Returns perpendicular vector. Perpendicular vector is computed as cross product between input vector ...
Definition EnConvert.c:209
proto native CGame GetGame()
const int COMP_TYPE_ENERGY_MANAGER
Definition Component.c:9
void Error(string err)
Messagebox with error message.
Definition EnDebug.c:90
proto void DPrint(string var)
Prints content of variable to console/log. Should be used for critical messages so it will appear in ...
class DiagMenu Shape
don't call destructor directly. Use Destroy() instead
array< int > TIntArray
Definition EnScript.c:668
static proto float Lerp(float a, float b, float time)
Linearly interpolates between 'a' and 'b' given 'time'.
static proto float Min(float x, float y)
Returns smaller of two given values.
static proto float Round(float f)
Returns mathematical round of value.
static proto float Clamp(float value, float min, float max)
Clamps 'value' to 'min' if it is lower than 'min', or to 'max' if it is higher than 'max'.
const int CALL_CATEGORY_SYSTEM
Definition tools.c:8