Dayz Explorer  1.24.157551 (v105080)
Dayz Code Explorer by Zeroy
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
22  protected Shape m_DebugPlugArrow;
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;
32  protected bool m_ShowSocketsInInventory;
33  protected bool m_HasElectricityIcon; // Electricity icon over the item in inventory
34  protected bool m_AutoSwitchOffWhenInCargo;
35  protected bool m_IsPlugged; // Synchronized variable
36  protected bool m_ConvertEnergyToQuantity;
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
44  protected int m_AttachmentActionType;
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;
52  protected float m_ReduceMaxEnergyByDamageCoef;
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 
68  protected ref TIntArray m_CompatiblePlugTypes;
69  EntityAI m_EnergySource; // Energy source can be any EntityAI object
70  ref array<EntityAI> m_PluggedDevices;
71  ref map<string,EntityAI> m_DeviceByPlugSelection;
72 
73  ref Timer m_UpdateTimer;
74  ref Timer m_UpdateQuantityTimer;
75  ref Timer m_DebugUpdate;
76 
77  const int MAX_SOCKETS_COUNT = 4;
78  EntityAI m_Sockets[MAX_SOCKETS_COUNT];
79 
80 
81 
82  // Constructor
84  {
85  // Disable debug arrows on public release, so that they don't use their timers.
86  #ifndef DEVELOPER
87  m_DebugPlugs = false;
88  #endif
89  }
90 
92  {
93  if (m_DebugPlugArrow)
94  {
95  m_DebugPlugArrow.Destroy();
96  m_DebugPlugArrow = NULL;
97  }
98  }
99 
100  // Initialization. Energy Manager is ready.
101  override void Event_OnInit()
102  {
103  m_ThisEntityAI.m_EM = this;
104  GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnInitEnergy", NULL, 0);
105  }
106 
107  // Update debug arrows
108  void DebugUpdate()
109  {
110  if ( GetDebugPlugs() )
111  {
112  if ( GetGame().IsMultiplayer() && GetGame().IsServer() )
113  {
114  if (m_DebugUpdate)
115  m_DebugUpdate.Stop();
116 
117  return;
118  }
119 
120  if (m_DebugPlugArrow)
121  {
122  m_DebugPlugArrow.Destroy();
123  m_DebugPlugArrow = NULL;
124  }
125 
126  if ( GetEnergySource() )
127  {
128  vector from = GetEnergySource().GetPosition() + "0 0.1 0";
129  vector to = m_ThisEntityAI.GetPosition() + "0 0.1 0";
130 
131  //No need to draw an arrow in this situation as it would not be visible
132  if ( vector.DistanceSq(from, to) == 0 )
133  return;
134 
135  if ( m_ThisEntityAI.GetType() == "BarbedWire" ) // Special case for debugging of electric fences. Temporal code until offsets in fences are fixed.
136  {
137  EntityAI BBB = m_ThisEntityAI.GetHierarchyParent();
138 
139  if ( BBB && BBB.GetType() == "Fence" )
140  {
141  to = to + "0 -1.3 0";
142  }
143  }
144 
145  m_DebugPlugArrow = DrawArrow( from, to );
146  }
147  }
148  }
149 
150  Shape DrawArrow(vector from, vector to, float size = 0.5, int color = 0xFFFFFFFF, float flags = 0)
151  {
152  vector dir = to - from;
153  dir.Normalize();
154  vector dir1 = dir * size;
155  size = size * 0.5;
156 
157  vector dir2 = dir.Perpend() * size;
158 
159  vector pts[5];
160  pts[0] = from;
161  pts[1] = to;
162  pts[2] = to - dir1 - dir2;
163  pts[3] = to - dir1 + dir2;
164  pts[4] = to;
165 
166  return Shape.CreateLines(color, flags, pts, 5);
167  }
168 
169  EntityAI GetThisEntityAI()
170  {
171  return m_ThisEntityAI;
172  }
173 
174  // Prepare everything
175  override void Event_OnAwake()
176  {
177  string cfg_item = "CfgVehicles " + m_ThisEntityAI.GetType();
178  string cfg_energy_manager = cfg_item + " EnergyManager ";
179 
180  // Read all config parameters
181  m_EnergyUsage = GetGame().ConfigGetFloat(cfg_energy_manager + "energyUsagePerSecond");
182  bool switch_on = GetGame().ConfigGetFloat(cfg_energy_manager + "switchOnAtSpawn");
183  m_AutoSwitchOff = GetGame().ConfigGetFloat(cfg_energy_manager + "autoSwitchOff");
184  m_HasElectricityIcon = GetGame().ConfigGetFloat(cfg_energy_manager + "hasIcon");
185  m_AutoSwitchOffWhenInCargo = GetGame().ConfigGetFloat(cfg_energy_manager + "autoSwitchOffWhenInCargo");
186 
187  m_EnergyAtSpawn = GetGame().ConfigGetFloat(cfg_energy_manager + "energyAtSpawn");
188  m_Energy = m_EnergyAtSpawn;
189  m_EnergyStorageMax = GetGame().ConfigGetFloat(cfg_energy_manager + "energyStorageMax");
190  m_ReduceMaxEnergyByDamageCoef = GetGame().ConfigGetFloat(cfg_energy_manager + "reduceMaxEnergyByDamageCoef");
191  m_SocketsCount = GetGame().ConfigGetFloat(cfg_energy_manager + "powerSocketsCount");
192 
193  m_IsPassiveDevice = GetGame().ConfigGetFloat(cfg_energy_manager + "isPassiveDevice");
194  m_CordLength = GetGame().ConfigGetFloat(cfg_energy_manager + "cordLength");
195  m_PlugType = GetGame().ConfigGetFloat(cfg_energy_manager + "plugType");
196 
197  m_AttachmentActionType = GetGame().ConfigGetFloat(cfg_energy_manager + "attachmentAction");
198  m_WetnessExposure = GetGame().ConfigGetFloat(cfg_energy_manager + "wetnessExposure");
199 
200  float update_interval = GetGame().ConfigGetFloat(cfg_energy_manager + "updateInterval");
201 
202  m_ConvertEnergyToQuantity = GetGame().ConfigGetFloat(cfg_energy_manager + "convertEnergyToQuantity");
203 
204 
205  // Check if energy->quantity converion is configured properly
206  float cfg_max_quantity = GetGame().ConfigGetFloat (cfg_item + " varQuantityMax");
207 
208  if (m_ConvertEnergyToQuantity && cfg_max_quantity <= 0)
209  {
210  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!";
211  Error(error);
212  m_ConvertEnergyToQuantity = false;
213  }
214  else
215  {
216  if (m_ConvertEnergyToQuantity)
217  {
218  if (!m_UpdateQuantityTimer)
219  m_UpdateQuantityTimer = new Timer( CALL_CATEGORY_SYSTEM );
220 
221  m_UpdateQuantityTimer.Run( 0.3 , this, "OnEnergyAdded", NULL, false);
222  }
223  }
224 
225  // Set update interval
226  if ( update_interval <= 0 )
227  update_interval = DEFAULT_UPDATE_INTERVAL;
228 
229  SetUpdateInterval( update_interval );
230 
231  // If energyAtSpawn is present, then use its value for energyStorageMax if that cfg param is not present (for convenience's sake)
232  string cfg_check_energy_limit = cfg_energy_manager + "energyStorageMax";
233 
234  if ( !GetGame().ConfigIsExisting (cfg_check_energy_limit) && m_Energy > 0 )
235  {
236  m_EnergyStorageMax = m_Energy;
237  }
238 
239  // Fill m_CompatiblePlugTypes
240  string cfg_check_plug_types = cfg_energy_manager + "compatiblePlugTypes";
241 
242  if ( GetGame().ConfigIsExisting (cfg_check_plug_types) )
243  {
244  m_CompatiblePlugTypes = new TIntArray;
245  GetGame().ConfigGetIntArray(cfg_check_plug_types, m_CompatiblePlugTypes);
246  }
247 
248  if (GetSocketsCount() > 0)
249  m_PluggedDevices = new array<EntityAI>;
250 
251  if ( m_CordLength < 0 )
252  {
253  m_CordLength = 0;
254  string error_message_cord = "Warning! " + m_ThisEntityAI.GetType() + ": config parameter 'cordLength' is less than 0! Cord length should not be negative!";
255  DPrint(error_message_cord);
256  }
257 
258  if (GetSocketsCount() > 0)
259  {
260  m_DeviceByPlugSelection = new map<string,EntityAI>;
261  // Prepare the m_DeviceByPlugSelection
262  string cfg_animation_sources = "cfgVehicles " + m_ThisEntityAI.GetType() + " " + "AnimationSources ";
263  int animation_sources_count = GetGame().ConfigGetChildrenCount(cfg_animation_sources);
264 
265  for (int i_selection = 0; i_selection < animation_sources_count; i_selection++)
266  {
267  // TO DO: This could be optimized so not all selections on item are considered as plug/socket selections.
268  string selection;
269  GetGame().ConfigGetChildName(cfg_animation_sources, i_selection, selection);
270  m_DeviceByPlugSelection.Set(selection, NULL);
271  }
272  }
273 
274 
275 
276  // Prepare sockets
277  if ( m_SocketsCount > MAX_SOCKETS_COUNT )
278  {
279  m_SocketsCount = MAX_SOCKETS_COUNT;
280  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!";
281  DPrint(error_message_sockets);
282  }
283 
284  m_Sockets[MAX_SOCKETS_COUNT]; // Handles selections for plugs in the sockets. Feel free to change the limit if needed.
285 
286  GetGame().ConfigGetText(cfg_energy_manager + "cordTextureFile", m_CordTextureFile);
287 
288  if ( switch_on )
289  {
290  SwitchOn();
291  }
292 
293  for ( int i = 0; i <= GetSocketsCount(); ++i )
294  {
295  m_ThisEntityAI.HideSelection ( SOCKET_ + i.ToString() + _PLUGGED );
296  }
297 
298  // Show/hide inventory sockets
299  m_ShowSocketsInInventory = false;
300  if ( GetSocketsCount() > 0 && IsPlugCompatible(PLUG_COMMON_APPLIANCE) && m_ThisEntityAI.GetType() != "MetalWire" ) // metal wire filter is hopefully temporal.
301  {
302  m_ShowSocketsInInventory = true;
303  }
304 
305  m_CanWork = HasEnoughStoredEnergy();
306 
307  m_ThisEntityAI.HideSelection( SEL_CORD_PLUGGED );
308 
309 
310  #ifdef DIAG_DEVELOPER
311  GetGame().m_EnergyManagerArray.Insert( this );
312  #endif
313  }
314 
315  // Returns the type of this component
316  override int GetCompType()
317  {
319  }
320 
321  // When the object is deleted
322  void OnDeviceDestroyed()
323  {
324  bool was_working = m_ThisEntityAI.GetCompEM().IsWorking();
325 
326  SwitchOff();
327  UnplugAllDevices();
328  UnplugThis();
329  SetPowered( false );
330 
331  if ( was_working )
332  m_ThisEntityAI.OnWorkStop();
333  ;
334  }
335 
336  //Restart the debug timer when relogging
337  void RefreshDebug()
338  {
339  if ( m_DebugPlugs )
340  {
341  if ( !m_DebugUpdate )
342  m_DebugUpdate = new Timer( CALL_CATEGORY_SYSTEM );
343 
344  if ( !m_DebugUpdate.IsRunning() )
345  m_DebugUpdate.Run(0.01, this, "DebugUpdate", NULL, true);
346  }
347  else
348  {
349  if ( m_DebugPlugArrow )
350  {
351  m_DebugPlugArrow.Destroy();
352  m_DebugPlugArrow = NULL;
353  }
354  }
355  }
356 
357  bool GetDebugPlugs()
358  {
359  return m_DebugPlugs;
360  }
361 
362  void SetDebugPlugs( bool newVal )
363  {
364  m_DebugPlugs = newVal;
365  RefreshDebug();
366  }
367 
368  //======================================================================================
369  // PUBLIC FUNCTIONS
370  // Use these to control the Energy Manager
371  // Functions are in order of their return value: void, bool, int, float, string, array.
372  //======================================================================================
373 
375  void SwitchOn()
376  {
377  m_IsSwichedOnPreviousState = m_IsSwichedOn;
378 
379  if (GetGame().IsServer() || !GetGame().IsMultiplayer())
380  {
381  if ( CanSwitchOn() )
382  {
383  m_IsSwichedOn = true;
384  Synch();
385 
386  DeviceUpdate(); // 'Wake up' this device now
387  StartUpdates();
388 
389  // 'Wakes up' all connected devices
390  WakeUpWholeBranch( m_ThisEntityAI );
391 
392  UpdateCanWork();
393 
394  // Call event
395  GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnSwitchOn", NULL, 0);
396  }
397  }
398 
399  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.)
400  {
401  GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnSwitchOn", NULL, 0);
402  }
403  }
404 
406  void SwitchOff()
407  {
408  m_IsSwichedOnPreviousState = m_IsSwichedOn;
409 
410  if (GetGame().IsServer() || !GetGame().IsMultiplayer())
411  {
412  if ( CanSwitchOff() )
413  {
414  m_IsSwichedOn = false;
415  Synch();
416 
417  if ( IsWorking() )
418  {
419  StopUpdates();
420  DeviceUpdate();
421  }
422 
423  // 'Wakes up' all connected devices
424  WakeUpWholeBranch( m_ThisEntityAI );
425 
426  UpdateCanWork();
427 
428  // Call event
429  GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnSwitchOff", NULL, 0);
430  }
431  }
432 
433  if ( !GetGame().IsServer() && GetGame().IsMultiplayer() )
434  {
435  m_IsSwichedOn = false;
436  GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnSwitchOff", NULL, 0);
437  }
438  }
439 
441  void SetPassiveState(bool state = true)
442  {
443  m_IsPassiveDevice = state;
444  if ( !m_IsPassiveDevice )
445  {
446  DeviceUpdate();
447  }
448  }
449 
451  void UnplugDevice(EntityAI device_to_unplug)
452  {
453  if ( GetGame() )
454  {
455  int indexStart = GetPluggedDevicesCount() - 1;
456  bool deviceFound = false;
457 
458  for (int i = indexStart; i >= 0; --i)
459  {
460  EntityAI plugged_device = GetPluggedDevices().Get(i);
461 
462  if (plugged_device == device_to_unplug)
463  {
464  GetPluggedDevices().Remove(i);
465  deviceFound = true;
466  break;
467  }
468  }
469 
470  if (deviceFound)
471  {
472  int socket_ID = device_to_unplug.GetCompEM().GetMySocketID();
473  UnplugCordFromSocket(socket_ID);
474  device_to_unplug.GetCompEM().SetEnergySource(null);
475  device_to_unplug.GetCompEM().DeviceUpdate();
476  device_to_unplug.GetCompEM().StartUpdates();
477  device_to_unplug.GetCompEM().WakeUpWholeBranch(m_ThisEntityAI);
478 
479  if (m_DebugPlugs && m_DebugPlugArrow)
480  {
481  m_DebugPlugArrow.Destroy();
482  m_DebugPlugArrow = null;
483  }
484 
485  OnOwnSocketReleased(device_to_unplug);
486  device_to_unplug.GetCompEM().OnIsUnplugged(m_ThisEntityAI);
487  device_to_unplug.ShowSelection(SEL_CORD_FOLDED);
488  device_to_unplug.HideSelection(SEL_CORD_PLUGGED);
489  }
490  }
491  }
492 
494  void UnplugThis()
495  {
496  if (GetGame())
497  {
498  if (GetEnergySource())
499  {
500  GetEnergySource().GetCompEM().UnplugDevice(m_ThisEntityAI);
501  }
502  }
503  }
504 
506  void UnplugAllDevices()
507  {
508  if ( GetPluggedDevices() ) // This check is necesarry in case this function is called before initialization
509  {
510  int indexStart = GetPluggedDevicesCount() - 1;
511  for (int i = indexStart; i >= 0; --i)
512  {
513  UnplugDevice(GetPluggedDevices().Get(i));
514  }
515  }
516  }
517 
518  // Used only for storing of the plug's state through server restart
519  void RestorePlugState(bool state)
520  {
521  m_RestorePlugState = state;
522  }
523 
525  void SetEnergy(float new_energy)
526  {
527  if (GetGame().IsServer() || !GetGame().IsMultiplayer()) // Client can't change energy value.
528  {
529  m_ThisEntityAI.SetWeightDirty();
530  float old_energy = m_Energy;
531  m_Energy = new_energy;
532 
533  if ( old_energy - GetEnergyUsage() <= 0 || (old_energy != new_energy && Math.Min(old_energy,new_energy) <= 0) )
534  {
535  UpdateCanWork();
536  }
537  }
538  }
539 
541  void SetEnergy0To1(float energy01)
542  {
543  SetEnergy( Math.Lerp(0, GetEnergyMax(),energy01));
544  }
545 
547  void UpdateSelections()
548  {
549  // Lets update sockets, if there are any
550  int slots_c = GetSocketsCount();
551 
552  for ( int i = 0; i < slots_c; ++i )
553  {
554  EntityAI plug_owner = GetDeviceBySocketID(i);
555 
556  if ( plug_owner )
557  {
558  string plugged_selection = SOCKET_ + (i+1).ToString() + _PLUGGED;
559  string available_selection = SOCKET_ + (i+1).ToString() + _AVAILABLE;
560  m_ThisEntityAI.ShowSelection ( plugged_selection );
561  m_ThisEntityAI.HideSelection ( available_selection );
562  string texture_path = plug_owner.GetCompEM().GetCordTextureFile();
563  int selection_index = m_ThisEntityAI.GetHiddenSelectionIndex( plugged_selection );
564  m_ThisEntityAI.SetObjectTexture(selection_index, texture_path );
565  }
566  else
567  {
568  m_ThisEntityAI.ShowSelection ( SOCKET_ + (i+1).ToString() + _AVAILABLE );
569  m_ThisEntityAI.HideSelection ( SOCKET_ + (i+1).ToString() + _PLUGGED );
570  }
571  }
572 
573  // Now lets update the cord/plug state
574  if ( GetEnergySource() )
575  {
576  m_ThisEntityAI.ShowSelection ( SEL_CORD_PLUGGED );
577  m_ThisEntityAI.HideSelection ( SEL_CORD_FOLDED );
578  }
579  else
580  {
581  m_ThisEntityAI.ShowSelection ( SEL_CORD_FOLDED );
582  m_ThisEntityAI.HideSelection ( SEL_CORD_PLUGGED );
583  }
584  }
585 
587  void UpdatePlugState()
588  {
589  if (m_ThisEntityAI.GetCompEM().GetEnergySource())
590  {
591  EntityAI player = m_ThisEntityAI.GetHierarchyRootPlayer();
592  // Check if the item is held in hands during advanced placement
593  if (player)
594  {
595  // Measure distance from the player
596  vector playerPosition = player.GetPosition();
597  if (!IsEnergySourceAtReach(playerPosition, 5))
598  UnplugThis();
599  }
600  else
601  {
602  // Measure distance from the device
603  vector itemPosition = m_ThisEntityAI.GetPosition();
604 
605  if (m_ThisEntityAI.GetHierarchyParent())
606  itemPosition = m_ThisEntityAI.GetHierarchyParent().GetPosition();
607 
608  if (!IsEnergySourceAtReach(itemPosition))
609  UnplugThis();
610  }
611  }
612  }
613 
614  // Returns an array of plug types this device can accept
615  void GetCompatiblePlugTypes(out TIntArray IDs)
616  {
617  IDs = m_CompatiblePlugTypes;
618  }
619 
620  // Stores IDs of the energy source.
621  void StoreEnergySourceIDs(int b1, int b2, int b3, int b4)
622  {
623  m_EnergySourceStorageIDb1 = b1;
624  m_EnergySourceStorageIDb2 = b2;
625  m_EnergySourceStorageIDb3 = b3;
626  m_EnergySourceStorageIDb4 = b4;
627  }
628 
630  void SetEnergyMaxPristine(float new_limit)
631  {
632  m_EnergyStorageMax = new_limit;
633  }
634 
636  void SetCordLength( float new_length )
637  {
638  m_CordLength = new_length;
639  }
640 
641  // Sets the plug type (for plug -> socket compatibility checks).
642  void SetPlugType( int new_type )
643  {
644  m_PlugType = new_type;
645  }
646 
647  // Sets the new attachment action type.
648  void SetAttachmentAction( int new_action_type )
649  {
650  m_AttachmentActionType = new_action_type;
651  }
652 
654  void SetEnergyUsage( float new_usage )
655  {
656  m_EnergyUsage = new_usage;
657  }
658 
660  void ResetEnergyUsage()
661  {
662  string cfg_energy_usage = "CfgVehicles " + m_ThisEntityAI.GetType() + " EnergyManager ";
663  m_EnergyUsage = GetGame().ConfigGetFloat (cfg_energy_usage + "energyUsagePerSecond");
664  }
665 
666  // Sets path to the cord texture file.
667  void SetCordTextureFile( string new_path )
668  {
669  m_CordTextureFile = new_path;
670  }
671 
672  // Sets energy source. Intended to be called only on client through RPC.
673  void SetEnergySourceClient( EntityAI source )
674  {
675  SetEnergySource(source);
676  }
677 
679  void SetDeviceBySocketID(int id, EntityAI plugged_device)
680  {
681  m_Sockets[id] = plugged_device;
682  }
683 
684 
686  void SetElectricityIconVisibility( bool make_visible )
687  {
688  m_HasElectricityIcon = make_visible;
689  }
690 
691  // 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.
692  void UpdateCanWork()
693  {
694  if (GetGame().IsServer() || !GetGame().IsMultiplayer())
695  {
696  bool current_state = CanWork();
697 
698  if (current_state != m_CanWork)
699  {
700  m_CanWork = current_state;
701  Synch();
702 
703  if ( m_ThisEntityAI && m_ThisEntityAI.GetHierarchyParent() && m_ThisEntityAI.GetHierarchyParent().GetCompEM() )
704  {
705  m_ThisEntityAI.GetHierarchyParent().GetCompEM().UpdateCanWork();
706  }
707  }
708  }
709  }
710 
711  void HandleMoveInsideCargo(EntityAI container)
712  {
713  if ( m_AutoSwitchOffWhenInCargo )
714  {
715  if (IsSwitchedOn())
716  {
717  SwitchOff();
718  }
719  }
720  }
721 
723  void SetUpdateInterval( float value )
724  {
725  m_UpdateInterval = value;
726  }
727 
728  // Returns true if this device was plugged into something at the end of previous session
729  bool GetRestorePlugState()
730  {
731  return m_RestorePlugState;
732  }
733 
735  bool PlugThisInto(EntityAI energy_source, int socket_id = -1)
736  {
737  return energy_source.GetCompEM().PlugInDevice(m_ThisEntityAI, socket_id);
738  }
739 
741  bool CanSwitchOn()
742  {
743  if ( !IsSwitchedOn() )
744  {
745  return true;
746  }
747 
748  return false;
749  }
750 
751 
757  bool CanWork( float test_energy = -1)
758  {
759  if ( GetGame().IsMultiplayer() && GetGame().IsClient() )
760  {
761  return m_CanWork;
762  }
763 
764  if (m_ThisEntityAI && m_ThisEntityAI.IsRuined())
765  {
766  return false;
767  }
768 
769  // Check if the power source(s) (which can be serially connected) can provide needed energy.
770  float energy_usage = test_energy;
771  float gathered_energy = GetEnergy();
772  EntityAI energy_source = GetEnergySource();
773 
774  if (energy_usage == -1)
775  {
776  energy_usage = GetEnergyUsage();
777  }
778 
779  if ( !CheckWetness() )
780  {
781  return false;
782  }
783 
784  if (gathered_energy <= 0 && energy_usage <= 0) //empty power source
785  {
786  return false;
787  }
788 
789  int cycle_limit = 500; // Sanity check to definitely avoid infinite cycles
790 
791  while ( gathered_energy < energy_usage ) // Look for energy source if we don't have enough stored energy
792  {
793  // Safetycheck!
794  if (cycle_limit > 0)
795  {
796  cycle_limit--;
797  }
798  else
799  {
800  DPrint("Energy Manager ERROR: The 'cycle_limit' safety break had to be activated to prevent possible game freeze. Dumping debug information...");
801  //Print(m_ThisEntityAI);
802  //Print(this);
803  //Print(energy_source);
804 
805  if (energy_source.GetCompEM())
806  {
807  //Print(energy_source.GetCompEM());
808  }
809 
810  //Print(gathered_energy);
811  //Print(energy_usage);
812 
813  //Print(m_ThisEntityAI.GetPosition());
814 
815  if (energy_source)
816  {
817  //Print(energy_source.GetPosition());
818  }
819 
820  //Print("End of the 'cycle_limit' safety break ^ ");
821 
822  return false;
823  }
824  // ^ Safetycheck!
825 
826  if ( energy_source && energy_source != m_ThisEntityAI && !energy_source.IsRuined() && energy_source.GetCompEM() && energy_source.GetCompEM().IsSwitchedOn() && energy_source.GetCompEM().CheckWetness() )
827  {
828  gathered_energy = gathered_energy + energy_source.GetCompEM().GetEnergy();
829  energy_source = energy_source.GetCompEM().GetEnergySource();
830  }
831  else
832  {
833  // No power source, no energy.
834  return false;
835  }
836  }
837 
838  // Enough energy was found
839  return true;
840  }
841 
843  bool CheckWetness()
844  {
845  return (m_ThisEntityAI.GetWet() <= 1-m_WetnessExposure);
846  }
847 
849  bool CanSwitchOff()
850  {
851  if ( IsPassive() )
852  {
853  return false;
854  }
855 
856  return IsSwitchedOn();
857  }
858 
859  // Returns previous state of the switch.
860  bool GetPreviousSwitchState()
861  {
862  return m_IsSwichedOnPreviousState;
863  }
864 
866  bool IsSwitchedOn()
867  {
868  return m_IsSwichedOn;
869  }
870 
872  bool IsCordFolded()
873  {
874  if ( IsPlugged() )
875  return false;
876 
877  return true;
878  }
879 
881  bool IsPassive()
882  {
883  return m_IsPassiveDevice;
884  }
885 
887  bool IsPlugged()
888  {
889  return m_IsPlugged;
890  }
891 
892 
894  bool ConsumeEnergy(float amount)
895  {
896  return FindAndConsumeEnergy(m_ThisEntityAI, amount, true);
897  }
898 
900  bool IsWorking()
901  {
902  return m_IsWorking;
903  }
904 
906  bool HasEnoughStoredEnergy()
907  {
908  if ( GetEnergy() > GetEnergyUsage() )
909  {
910  return true;
911  }
912 
913  return false;
914  }
915 
917  bool HasFreeSocket( int socket_id = -1 )
918  {
919  if (socket_id == -1)
920  {
921  int plugged_devices = GetPluggedDevicesCount();
922  int plugged_devices_limit = GetSocketsCount();
923 
924  if ( plugged_devices < plugged_devices_limit )
925  {
926  return true;
927  }
928 
929  return false;
930  }
931  else
932  {
933  EntityAI device = GetDeviceBySocketID(socket_id);
934 
935  if (device)
936  {
937  return false;
938  }
939  else
940  {
941  return true;
942  }
943  }
944  }
945 
947  bool IsPlugCompatible(int plug_ID)
948  {
949  if ( plug_ID == PLUG_UNDEFINED )
950  {
951  return true; // When plugType is undefined in config then make it compatible.
952  }
953 
954  if ( m_CompatiblePlugTypes )
955  {
956  for ( int i = 0; i < m_CompatiblePlugTypes.Count(); i++ )
957  {
958  int plug_ID_to_Check = m_CompatiblePlugTypes.Get(i);
959 
960  if ( plug_ID_to_Check == plug_ID )
961  {
962  return true;
963  }
964  }
965  }
966  else
967  {
968  // Since the config parameter compatiblePlugTypes is not present, then accept all plugs for simplicity's sake
969  return true;
970  }
971 
972  return false;
973  }
974 
976  bool CanReceivePlugFrom( EntityAI device_to_plug )
977  {
978  // The following conditions are broken down for the sake of easier reading/debugging.
979 
980  if ( HasFreeSocket() && device_to_plug != m_ThisEntityAI)
981  {
982  if ( device_to_plug.GetCompEM().GetEnergySource() != m_ThisEntityAI)
983  {
984  if ( IsPlugCompatible(device_to_plug.GetCompEM().GetPlugType()) )
985  {
986  if ( device_to_plug.GetCompEM().IsEnergySourceAtReach( device_to_plug.GetPosition(), 0, m_ThisEntityAI.GetPosition() ) )
987  {
988  return true;
989  }
990  }
991  }
992  }
993 
994  return false;
995  }
996 
998  bool CanBePluggedInto( EntityAI potential_energy_provider )
999  {
1000  return potential_energy_provider.GetCompEM().CanReceivePlugFrom( m_ThisEntityAI );
1001  }
1002 
1004  bool HasElectricityIcon()
1005  {
1006  return m_HasElectricityIcon;
1007  }
1008 
1010  bool HasConversionOfEnergyToQuantity()
1011  {
1012  return m_ConvertEnergyToQuantity;
1013  }
1014 
1031  bool IsEnergySourceAtReach( vector from_position, float add_tolerance = 0, vector override_source_position = "-1 -1 -1" )
1032  {
1033  if ( !IsPlugged() && override_source_position == "-1 -1 -1" )
1034  {
1035  return false;
1036  }
1037 
1038  if ( GetCordLength() == 0 ) // 0 is an exception, which means infinitely long cable.
1039  {
1040  return true;
1041  }
1042 
1043  vector source_pos;
1044  float distance;
1045 
1046  if ( override_source_position == "-1 -1 -1" )
1047  {
1048  EntityAI energy_source = GetEnergySource();
1049 
1050  if (!energy_source)
1051  return false;
1052 
1053  source_pos = energy_source.GetPosition();
1054  distance = vector.Distance( from_position, source_pos );
1055  }
1056  else
1057  {
1058  source_pos = override_source_position;
1059  distance = vector.Distance( from_position, source_pos );
1060  }
1061 
1062  if (distance > GetCordLength() + add_tolerance)
1063  {
1064  return false;
1065  }
1066  else
1067  {
1068  return true;
1069  }
1070  }
1071 
1072  bool HasVisibleSocketsInInventory()
1073  {
1074  return m_ShowSocketsInInventory;
1075  }
1076 
1078  bool IsSelectionAPlug(string selection_to_test )
1079  {
1080  if ( GetPluggedDevices() )
1081  {
1082  int socket_count = GetSocketsCount();
1083 
1084  for ( int i = socket_count; i >= 0; --i )
1085  {
1086  string real_selection = SOCKET_ + i.ToString() +_PLUGGED;
1087 
1088  if ( selection_to_test == real_selection)
1089  {
1090  return true;
1091  }
1092  }
1093  }
1094 
1095  return false;
1096  }
1097 
1098 
1099 
1100 
1102  int GetSocketsCount()
1103  {
1104  return m_SocketsCount;
1105  }
1106 
1108  int GetPlugType()
1109  {
1110  return m_PlugType;
1111  }
1112 
1113  // Returns the action ID which is supposed to be done upon receiving an attachment
1114  int GetAttachmentAction()
1115  {
1116  return m_AttachmentActionType;
1117  }
1118 
1119  // Returns persistent ID (block 1) of the energy source
1120  int GetEnergySourceStorageIDb1()
1121  {
1122  return m_EnergySourceStorageIDb1;
1123  }
1124 
1125  // Returns persistent ID (block 2) of the energy source
1126  int GetEnergySourceStorageIDb2()
1127  {
1128  return m_EnergySourceStorageIDb2;
1129  }
1130 
1131  // Returns persistent ID (block 3) of the energy source
1132  int GetEnergySourceStorageIDb3()
1133  {
1134  return m_EnergySourceStorageIDb3;
1135  }
1136 
1137  // Returns persistent ID (block 4) of the energy source
1138  int GetEnergySourceStorageIDb4()
1139  {
1140  return m_EnergySourceStorageIDb4;
1141  }
1142 
1143  // Returns network ID (low) of the energy source
1144  int GetEnergySourceNetworkIDLow()
1145  {
1146  return m_EnergySourceNetworkIDLow;
1147  }
1148 
1149  // Returns network ID (high) of the energy source
1150  int GetEnergySourceNetworkIDHigh()
1151  {
1152  return m_EnergySourceNetworkIDHigh;
1153  }
1154 
1156  int GetPluggedDevicesCount()
1157  {
1158  if ( GetPluggedDevices() )
1159  {
1160  return GetPluggedDevices().Count();
1161  }
1162 
1163  return 0;
1164  }
1165 
1167  int GetEnergy0To100()
1168  {
1169  if ( m_EnergyStorageMax > 0 )
1170  {
1171  int coef = Math.Round( m_Energy / m_EnergyStorageMax * 100 );
1172  return coef;
1173  }
1174 
1175  return 0;
1176  }
1177 
1179  float GetEnergy0To1()
1180  {
1181  if ( m_EnergyStorageMax > 0 )
1182  {
1183  return m_Energy / m_EnergyStorageMax;
1184  }
1185 
1186  return 0;
1187  }
1188 
1190  float GetUpdateInterval()
1191  {
1192  #ifdef DIAG_DEVELOPER
1193  if (FeatureTimeAccel.GetFeatureTimeAccelEnabled(ETimeAccelCategories.ENERGY_CONSUMPTION) || (FeatureTimeAccel.GetFeatureTimeAccelEnabled(ETimeAccelCategories.ENERGY_RECHARGE)))
1194  {
1195  return 1;//when modifying time accel, we might want to see things happen when they should, instead of waiting for the next tick
1196  }
1197  #endif
1198  return m_UpdateInterval;
1199  }
1200 
1202  float GetWetnessExposure()
1203  {
1204  return m_WetnessExposure;
1205  }
1206 
1208  float GetEnergyUsage()
1209  {
1210  return m_EnergyUsage;
1211  }
1212 
1214  float GetEnergy()
1215  {
1216  return m_Energy;
1217  }
1218 
1220  float AddEnergy(float added_energy)
1221  {
1222  if (added_energy != 0)
1223  {
1224  //Print("AddEnergy ---------> " + added_energy + " " + this + " " +m_ThisEntityAI.ClassName());
1225  #ifdef DIAG_DEVELOPER
1226  if (FeatureTimeAccel.GetFeatureTimeAccelEnabled(ETimeAccelCategories.ENERGY_CONSUMPTION) && added_energy < 0)
1227  {
1228  float timeAccel = FeatureTimeAccel.GetFeatureTimeAccelValue();
1229  added_energy *= timeAccel;
1230  }
1231  #endif
1232 
1233  bool energy_was_added = (added_energy > 0);
1234 
1235  float energy_to_clamp = GetEnergy() + added_energy;
1236  float clamped_energy = Math.Clamp( energy_to_clamp, 0, GetEnergyMax() );
1237  SetEnergy(clamped_energy);
1238  StartUpdates();
1239 
1240  if (energy_was_added)
1241  OnEnergyAdded();
1242  else
1243  OnEnergyConsumed();
1244 
1245  return energy_to_clamp - clamped_energy;
1246  }
1247 
1248  return 0;
1249  }
1250 
1252  float GetEnergyMax()
1253  {
1254  float max_health = 0;
1255 
1256  if ( m_ThisEntityAI.HasDamageSystem() )
1257  max_health = m_ThisEntityAI.GetMaxHealth("","");
1258  //else if ( m_ReduceMaxEnergyByDamageCoef != 0 )
1259  // Error("[ERROR] ReduceMaxEnergyByDamageCoef is setup but " + m_ThisEntityAI.GetType() + " does not have a Damage System");
1260 
1261  if ( max_health == 0 || m_ReduceMaxEnergyByDamageCoef == 0 )
1262  return GetEnergyMaxPristine();
1263 
1264  float health = 100;
1265 
1266  if (GetGame().IsServer() || !GetGame().IsMultiplayer()) // TO DO: Remove this IF when method GetHealth can be called on client!
1267  health = m_ThisEntityAI.GetHealth("","");
1268 
1269  float damage_coef = 1 - (health / max_health);
1270 
1271  return GetEnergyMaxPristine() * (1 - ( damage_coef * m_ReduceMaxEnergyByDamageCoef ) );
1272  }
1273 
1275  float GetEnergyMaxPristine()
1276  {
1277  return m_EnergyStorageMax;
1278  }
1279 
1280  float GetEnergyAtSpawn()
1281  {
1282  return m_EnergyAtSpawn;
1283  }
1284 
1286  float GetCordLength()
1287  {
1288  return m_CordLength;
1289  }
1290 
1292  EntityAI GetEnergySource()
1293  {
1294  return m_EnergySource;
1295  }
1296 
1298  EntityAI GetDeviceBySocketID(int id)
1299  {
1300  return m_Sockets[id];
1301  }
1302 
1304  EntityAI GetPlugOwner(string plug_selection_name)
1305  {
1306  if ( m_DeviceByPlugSelection.Contains(plug_selection_name) )
1307  {
1308  return m_DeviceByPlugSelection.Get(plug_selection_name);
1309  }
1310 
1311  return NULL;
1312  }
1313 
1315  EntityAI GetPluggedDevice()
1316  {
1317  if ( GetPluggedDevicesCount() > 0 )
1318  {
1319  return GetPluggedDevices().Get(0);
1320  }
1321 
1322  return NULL;
1323  }
1324 
1326  string GetCordTextureFile()
1327  {
1328  return m_CordTextureFile;
1329  }
1330 
1332  array<EntityAI> GetPluggedDevices()
1333  {
1334  return m_PluggedDevices;
1335  }
1336 
1338  array<EntityAI> GetPoweredDevices()
1339  {
1340  array<EntityAI> return_array = new array<EntityAI>;
1341  int plugged_devices_c = GetPluggedDevicesCount();
1342  for ( int i = 0; i < plugged_devices_c; ++i )
1343  {
1344  EntityAI device = GetPluggedDevices().Get(i);
1345  if ( IsSwitchedOn() )
1346  {
1347  return_array.Insert(device);
1348  }
1349  }
1350 
1351  return return_array;
1352  }
1353 
1354 
1355  /*===================================
1356  PUBLIC EVENTS
1357  ===================================*/
1358 
1359  // Called every device update if its supposed to do some work. The update can be every second or at random, depending on its manipulation.
1360  void OnWork( float consumed_energy )
1361  {
1362  m_ThisEntityAI.OnWork(consumed_energy);
1363  }
1364 
1365  // Called when this device is plugged into some energy source
1366  void OnIsPlugged(EntityAI source_device)
1367  {
1368  if (m_DebugPlugs)
1369  {
1370  if (!m_DebugUpdate)
1371  m_DebugUpdate = new Timer( CALL_CATEGORY_SYSTEM );
1372 
1373  if (!m_DebugUpdate.IsRunning())
1374  m_DebugUpdate.Run(0.01, this, "DebugUpdate", NULL, true);
1375  }
1376 
1377  UpdateCanWork();
1378  m_ThisEntityAI.OnIsPlugged(source_device);
1379  }
1380 
1381  // Called when this device is UNPLUGGED from the energy source
1382  void OnIsUnplugged( EntityAI last_energy_source )
1383  {
1384  UpdateCanWork();
1385  m_ThisEntityAI.OnIsUnplugged( last_energy_source );
1386  }
1387 
1388  // When something is plugged into this device
1389  void OnOwnSocketTaken( EntityAI device )
1390  {
1391  //play sound
1392  if ( device.GetCompEM().GetPlugType() == PLUG_COMMON_APPLIANCE && m_ThisEntityAI.IsInitialized() )
1393  {
1394  EffectSound sound_plug;
1395  m_ThisEntityAI.PlaySoundSet( sound_plug, "cablereel_plugin_SoundSet", 0, 0 );
1396  }
1397 
1398  m_ThisEntityAI.OnOwnSocketTaken(device);
1399  }
1400 
1401  // When something is UNPLUGGED from this device
1402  void OnOwnSocketReleased( EntityAI device )
1403  {
1404  //play sound
1405  if ( device.GetCompEM().GetPlugType() == PLUG_COMMON_APPLIANCE && m_ThisEntityAI.IsInitialized() )
1406  {
1407  EffectSound sound_unplug;
1408  m_ThisEntityAI.PlaySoundSet( sound_unplug, "cablereel_unplug_SoundSet", 0, 0 );
1409  }
1410 
1411  m_ThisEntityAI.OnOwnSocketReleased( device );
1412  }
1413 
1414 
1415  // Handles automatic attachment action
1416  void OnAttachmentAdded(EntityAI elec_device)
1417  {
1418  int attachment_action_type = GetAttachmentAction();
1419 
1420  if ( attachment_action_type == PLUG_THIS_INTO_ATTACHMENT )
1421  {
1422  if ( elec_device.GetCompEM().CanReceivePlugFrom( m_ThisEntityAI ) )
1423  {
1424  PlugThisInto(elec_device);
1425  }
1426  }
1427  else if ( attachment_action_type == PLUG_ATTACHMENTS_INTO_THIS )
1428  {
1429  elec_device.GetCompEM().PlugThisInto(m_ThisEntityAI);
1430  }
1431  }
1432 
1433  // Handles automatic detachment action
1434  void OnAttachmentRemoved(EntityAI elec_device)
1435  {
1436  int attachment_action_type = GetAttachmentAction();
1437 
1438  if ( attachment_action_type == PLUG_THIS_INTO_ATTACHMENT )
1439  {
1440  if ( elec_device == GetEnergySource() )
1441  {
1442  UnplugThis();
1443  }
1444  }
1445  else if ( attachment_action_type == PLUG_ATTACHMENTS_INTO_THIS )
1446  {
1447  elec_device.GetCompEM().UnplugThis();
1448  }
1449  }
1450 
1451  // Starts the device's main cycle
1452  void StartUpdates()
1453  {
1454  if (!m_IsPassiveDevice)
1455  {
1456  if (!m_UpdateTimer)
1458 
1459  if (!m_UpdateTimer.IsRunning()) // Makes sure the timer is NOT running already
1460  {
1461  m_UpdateTimer.Run(GetUpdateInterval(), this, "DeviceUpdate", null, true);
1462  }
1463  }
1464  }
1465 
1467  void OnEnergyConsumed()
1468  {
1469  m_ThisEntityAI.OnEnergyConsumed();
1470  }
1471 
1473  void OnEnergyAdded()
1474  {
1475  if (m_UpdateQuantityTimer)
1476  {
1477  m_UpdateQuantityTimer.Stop();
1478  m_UpdateQuantityTimer = NULL;
1479  }
1480 
1481  m_ThisEntityAI.OnEnergyAdded();
1482  }
1483 
1484 
1485  /*===================================
1486  PROTECTED FUNCTIONS
1487  ===================================*/
1488 
1489  // Stops the device's main cycle
1490  protected void StopUpdates()
1491  {
1492  if (m_UpdateTimer)
1493  {
1494  m_UpdateTimer.Stop();
1495  m_UpdateTimer = NULL; // Delete timer from memory
1496  }
1497  }
1498 
1502  void InteractBranch(EntityAI originalCaller, Man player = null, int system = 0)
1503  {
1504  OnInteractBranch(originalCaller, player, system);
1505  if ( GetSocketsCount() > 0 )
1506  {
1507  array<EntityAI> devices = GetPluggedDevices();
1508 
1509  foreach ( EntityAI device : devices)
1510  {
1511  if ( device != originalCaller ) // originalCaller check here prevents infinite loops
1512  {
1513  device.GetCompEM().InteractBranch( originalCaller, player, system );
1514  }
1515  }
1516  }
1517  }
1518 
1520  protected void OnInteractBranch(EntityAI originalCaller, Man player, int system)
1521  {
1522  m_ThisEntityAI.IncreaseLifetime();
1523 
1524  }
1525 
1526  // 'Wakes up' all devices down the network so they start working, if they have enough power, and are switched ON
1527  protected void WakeUpWholeBranch( EntityAI original_caller )
1528  {
1529  if ( GetSocketsCount() > 0 )
1530  {
1531  array<EntityAI> plugged_devices = GetPluggedDevices();
1532  int plugged_devices_c = plugged_devices.Count();
1533 
1534  for ( int i = 0; i < plugged_devices_c; ++i )
1535  {
1536  EntityAI device = plugged_devices.Get(i);
1537  if ( device != original_caller ) // original_caller check here prevents infinite loops
1538  {
1539  device.GetCompEM().UpdateCanWork();
1540  device.GetCompEM().DeviceUpdate();
1541  device.GetCompEM().StartUpdates();
1542  device.GetCompEM().WakeUpWholeBranch( original_caller );
1543  }
1544  }
1545  }
1546  }
1547 
1548  // Finds an available socket and plugs the given device into it.
1549  // This is mainly about visualisation.
1550  protected void PlugCordIntoSocket( EntityAI device_to_plug, int socket_id = -1 )
1551  {
1552  if (socket_id >= 0)
1553  {
1554  EntityAI plug_owner_by_socket = GetDeviceBySocketID(socket_id);
1555 
1556  if (!plug_owner_by_socket)
1557  {
1558  UpdateSocketSelections(socket_id, device_to_plug);
1559  return;
1560  }
1561  }
1562 
1563  int slots_c = GetSocketsCount();
1564 
1565  for ( int i = 0; i < slots_c; ++i )
1566  {
1567  EntityAI plug_owner = GetDeviceBySocketID(i);
1568 
1569  if ( !plug_owner ) // Check if this socket is available
1570  {
1571  UpdateSocketSelections(i, device_to_plug);
1572  break;
1573  }
1574  }
1575  }
1576 
1577  // Updates socket selections (plugged/unplugged) of the given ID and sets color texture of the plug.
1578  protected void UpdateSocketSelections(int socket_id, EntityAI device_to_plug)
1579  {
1580  SetDeviceBySocketID(socket_id, device_to_plug);
1581 
1582  string plugged_selection = SOCKET_ + (socket_id+1).ToString() + _PLUGGED;
1583  SetPlugOwner( plugged_selection, device_to_plug );
1584  m_ThisEntityAI.ShowSelection ( plugged_selection );
1585 
1586  string unplugged_selection = SOCKET_ + (socket_id+1).ToString() + _AVAILABLE;
1587  m_ThisEntityAI.HideSelection ( unplugged_selection );
1588  string texture_path = device_to_plug.GetCompEM().GetCordTextureFile();
1589  int selection_index = m_ThisEntityAI.GetHiddenSelectionIndex( plugged_selection );
1590  m_ThisEntityAI.SetObjectTexture( selection_index, texture_path );
1591  device_to_plug.GetCompEM().SetMySocketID(socket_id);
1592  }
1593 
1594 
1595  // Sets energy source for this device
1596  protected void SetEnergySource( EntityAI source )
1597  {
1598  m_EnergySource = source;
1599 
1600  if (source)
1601  {
1602  m_IsPlugged = true;
1603  StartUpdates();
1604  }
1605  else
1606  {
1607  m_IsPlugged = false;
1608  m_EnergySourceNetworkIDLow = -1;
1609  m_EnergySourceNetworkIDHigh = -1;
1610  }
1611 
1612  if (m_EnergySource)
1613  m_EnergySource.GetNetworkID(m_EnergySourceNetworkIDLow, m_EnergySourceNetworkIDHigh);
1614 
1615  Synch();
1616  }
1617 
1618  // Plugs the given device into this one
1619  protected bool PlugInDevice(EntityAI device_to_plug, int socket_id = -1)
1620  {
1621  if (CanReceivePlugFrom(device_to_plug))
1622  {
1623  device_to_plug.IncreaseLifetime();
1624  InteractBranch(m_ThisEntityAI);
1625  if (device_to_plug.GetCompEM().IsPlugged())
1626  device_to_plug.GetCompEM().UnplugThis();
1627 
1628  GetPluggedDevices().Insert(device_to_plug);
1629  device_to_plug.GetCompEM().SetEnergySource(m_ThisEntityAI);
1630 
1631  PlugCordIntoSocket(device_to_plug, socket_id); // Visualisation
1632  OnOwnSocketTaken(device_to_plug);
1633 
1634  device_to_plug.GetCompEM().OnIsPlugged(m_ThisEntityAI);
1635  WakeUpWholeBranch( m_ThisEntityAI );
1636 
1637  if (GetGame().IsServer() || !GetGame().IsMultiplayer())
1638  {
1639  device_to_plug.HideSelection(SEL_CORD_FOLDED);
1640  device_to_plug.ShowSelection(SEL_CORD_PLUGGED);
1641  }
1642 
1643  return true;
1644  }
1645 
1646  return false;
1647  }
1648 
1649  // Sets the device to which the given plug selection belongs to
1650  protected void SetPlugOwner(string selection_name, EntityAI device)
1651  {
1652  if ( m_DeviceByPlugSelection.Contains(selection_name) )
1653  {
1654  m_DeviceByPlugSelection.Set(selection_name, device);
1655  }
1656  }
1657 
1658  // Frees the given socket.
1659  // This is only about visualisation.
1660  protected void UnplugCordFromSocket( int socket_to_unplug_ID )
1661  {
1662  EntityAI plug_owner = GetDeviceBySocketID(socket_to_unplug_ID);
1663 
1664  if ( plug_owner )
1665  {
1666  SetDeviceBySocketID(socket_to_unplug_ID, NULL);
1667  string unplugged_selection = SOCKET_ + (socket_to_unplug_ID+1).ToString() + _AVAILABLE;
1668  m_ThisEntityAI.ShowSelection ( unplugged_selection );
1669 
1670  string plugged_selection = SOCKET_ + (socket_to_unplug_ID+1).ToString() + _PLUGGED;
1671  m_ThisEntityAI.HideSelection ( plugged_selection );
1672  SetPlugOwner( plugged_selection, NULL );
1673  plug_owner.GetCompEM().SetMySocketID(-1);
1674  }
1675  }
1676 
1677  // Sets the state of the device
1678  protected void SetPowered( bool state )
1679  {
1680  m_IsWorking = state;
1681  }
1682 
1683  // 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.
1684  protected bool FindAndConsumeEnergy(EntityAI original_caller, float amount, bool ignore_switch_state = false)
1685  {
1686  if ( (ignore_switch_state || IsSwitchedOn()) && !m_ThisEntityAI.IsRuined() )
1687  {
1688  float available_energy = AddEnergy(-amount);
1689 
1690  if ( available_energy < 0 && IsPlugged() )
1691  {
1692  // 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)
1693  EntityAI next_power_source = GetEnergySource();
1694 
1695  if (next_power_source && next_power_source != original_caller) // Prevents infinite loop if the power source is the original caller itself
1696  {
1697  return next_power_source.GetCompEM().FindAndConsumeEnergy( original_caller, -available_energy );
1698  }
1699  }
1700 
1701  if ( available_energy >= 0)
1702  {
1703  return true;
1704  }
1705 
1706  return false;
1707  }
1708  else
1709  {
1710  return false;
1711  }
1712  }
1713 
1714  // Gets the socket ID this device is powered from.
1715  protected int GetMySocketID()
1716  {
1717  return m_MySocketID;
1718  }
1719 
1720  // Sets the socket ID this device is plugged into.
1721  protected void SetMySocketID( int slot_ID )
1722  {
1723  m_MySocketID = slot_ID;
1724  }
1725 
1726  void Synch()
1727  {
1728  m_ThisEntityAI.SetSynchDirty();
1729  }
1730 
1731  void ClearLastUpdateTime()
1732  {
1733  m_LastUpdateTime = 0;
1734  }
1735 
1736  void RememberLastUpdateTime()
1737  {
1738  m_LastUpdateTime = GetCurrentUpdateTime();
1739  }
1740 
1741  float GetCurrentUpdateTime()
1742  {
1743  return GetGame().GetTime();
1744  }
1745 
1746  // Updates the device's state of power. This function is visualized in the diagram at DayZ Confluence >> Camping & Squatting >> Electricity >> Energy Manager functionalities
1747  void DeviceUpdate()
1748  {
1749  /*
1750  vector pos = m_ThisEntityAI.GetPosition();
1751  string debug_message = "Object " + m_ThisEntityAI.GetType() + " | Energy: " + GetEnergy() + " | IsAtReach: " + (IsEnergySourceAtReach(pos)).ToString();
1752  Print(debug_message);
1753  */
1754 
1755  if ( !m_IsPassiveDevice )
1756  {
1757  // 'm_ThisEntityAI' and 'this' must be checked because this method is caled from a timer
1758  if ( m_ThisEntityAI && this && IsSwitchedOn() && !m_ThisEntityAI.IsRuined() && CheckWetness() && m_CanWork && !GetGame().IsMissionMainMenu() )
1759  {
1760  bool was_powered = IsWorking();
1761  float consumed_energy_coef;
1762  // Make sure to use only as much % of energy as needed since this function can be called at random.
1763 
1764  if ( m_LastUpdateTime == 0 )
1765  {
1766  RememberLastUpdateTime();
1767  consumed_energy_coef = 1.0;
1768  }
1769  else
1770  {
1771  float updatetime = GetCurrentUpdateTime();
1772  float time = updatetime - m_LastUpdateTime;
1773  consumed_energy_coef = time / 1000;
1774  }
1775 
1776  if (consumed_energy_coef > 0) // Prevents calling of OnWork events when no energy is consumed
1777  {
1778  m_LastUpdateTime = GetCurrentUpdateTime();
1779  float consume_energy = GetEnergyUsage() * consumed_energy_coef;
1780  bool has_consumed_enough = true;
1781 
1782  if (GetGame().IsServer() || !GetGame().IsMultiplayer()) // single player or server side multiplayer
1783  has_consumed_enough = ConsumeEnergy( consume_energy );
1784 
1785  SetPowered( has_consumed_enough );
1786 
1787  if ( has_consumed_enough )
1788  {
1789  if ( !was_powered )
1790  {
1791  m_CanStopWork = true;
1792  WakeUpWholeBranch(m_ThisEntityAI);
1793  GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnWorkStart", NULL, 0);
1794  UpdateCanWork();
1795  }
1796 
1797  OnWork( consume_energy );
1798  }
1799  else
1800  {
1801  if ( was_powered )
1802  {
1803  if (m_CanStopWork)
1804  {
1805  m_CanStopWork = false;
1806  ClearLastUpdateTime();
1807  GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnWorkStop", NULL, 0); // This event is called only once when the device STOPS being powered
1808  UpdateCanWork();
1809 
1810  if (m_AutoSwitchOff)
1811  {
1812  SwitchOff();
1813  }
1814  }
1815  }
1816 
1817  StopUpdates();
1818  }
1819  }
1820  else
1821  {
1822  ClearLastUpdateTime();
1823  }
1824  }
1825  else if (this && m_ThisEntityAI)
1826  {
1827  SetPowered( false );
1828  StopUpdates();
1829 
1830  if (m_CanStopWork)
1831  {
1832  m_CanStopWork = false;
1833  ClearLastUpdateTime();
1834  GetGame().GameScript.CallFunction(m_ThisEntityAI, "OnWorkStop", NULL, 0); // This event is called only once when the device STOPS being powered
1835  UpdateCanWork();
1836 
1837  if (m_AutoSwitchOff)
1838  {
1839  SwitchOff();
1840  }
1841  }
1842  }
1843  }
1844  }
1845 }
GetGame
proto native CGame GetGame()
CALL_CATEGORY_SYSTEM
const int CALL_CATEGORY_SYSTEM
Definition: tools.c:8
DPrint
proto void DPrint(string var)
Prints content of variable to console/log. Should be used for critical messages so it will appear in ...
PLUG_ATTACHMENTS_INTO_THIS
const int PLUG_ATTACHMENTS_INTO_THIS
Definition: _constants.c:36
PLUG_COMMON_APPLIANCE
const int PLUG_COMMON_APPLIANCE
Definition: _constants.c:25
ComponentEnergyManager
Definition: componentenergymanager.c:18
Error
void Error(string err)
Messagebox with error message.
Definition: endebug.c:90
PLUG_UNDEFINED
const int PLUG_UNDEFINED
Definition: _constants.c:23
Component
Definition: componententitydebug.c:1
ToString
proto string ToString()
COMP_TYPE_ENERGY_MANAGER
const int COMP_TYPE_ENERGY_MANAGER
Definition: component.c:9
TIntArray
array< int > TIntArray
Definition: enscript.c:687
EffectSound
Wrapper class for managing sound through SEffectManager.
Definition: effectsound.c:4
map
map
Definition: controlsxboxnew.c:3
vector
Definition: enconvert.c:105
Get
array< ref PlayerStatBase > Get()
Definition: playerstatspco.c:103
array
Result for an object found in CGame.IsBoxCollidingGeometryProxy.
Definition: isboxcollidinggeometryproxyclasses.c:27
Timer
Definition: dayzplayerimplement.c:62
PLUG_THIS_INTO_ATTACHMENT
const int PLUG_THIS_INTO_ATTACHMENT
Definition: _constants.c:35
Math
Definition: enmath.c:6
EntityAI
Definition: building.c:5
Shape
class DiagMenu Shape
don't call destructor directly. Use Destroy() instead
m_UpdateTimer
protected ref Timer m_UpdateTimer
Definition: radialmenu.c:20