Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Items/Components/Signal/Connection.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Collections.Immutable;
4 using System.Linq;
5 using System.Xml.Linq;
6 
8 {
9  partial class Connection
10  {
11  //how many wires can be linked to connectors by default
12  private const int DefaultMaxWires = 5;
13 
14  //how many wires a player can link to this connection
15  public readonly int MaxPlayerConnectableWires = 5;
16 
17  //how many wires can be linked to this connection in total
18  public readonly int MaxWires = 5;
19 
20  public readonly string Name;
21  public readonly LocalizedString DisplayName;
22 
23  private readonly HashSet<Wire> wires;
24  public IReadOnlyCollection<Wire> Wires => wires;
25 
33  public List<CircuitBoxConnection> CircuitBoxConnections = new();
34 
35  private bool enumeratingWires;
36  private readonly HashSet<Wire> removedWires = new HashSet<Wire>();
37 
38  private readonly Item item;
39 
40  public readonly bool IsOutput;
41 
42  public readonly List<StatusEffect> Effects;
43 
44  public readonly List<(ushort wireId, int? connectionIndex)> LoadedWires;
45 
46  //The grid the connection is a part of
47  public GridInfo Grid;
48 
49  //Priority in which power output will be handled - load is unaffected
51 
52  public Signal LastSentSignal { get; private set; }
53  public Signal LastReceivedSignal {get; private set;}
54 
55  public bool IsPower
56  {
57  get;
58  private set;
59  }
60 
61  private bool recipientsDirty = true;
62  private readonly List<Connection> recipients = new List<Connection>();
63  public List<Connection> Recipients
64  {
65  get
66  {
67  if (recipientsDirty) { RefreshRecipients(); }
68  return recipients;
69  }
70  }
71 
72  public Item Item
73  {
74  get { return item; }
75  }
76 
78  {
79  get;
80  private set;
81  }
82 
83  public override string ToString()
84  {
85  return "Connection (" + item.Name + ", " + Name + ")";
86  }
87 
88  public Connection(ContentXElement element, ConnectionPanel connectionPanel, IdRemap idRemap)
89  {
90 
91 #if CLIENT
92  if (connector == null)
93  {
94  connector = GUIStyle.GetComponentStyle("ConnectionPanelConnector").GetDefaultSprite();
95  wireVertical = GUIStyle.GetComponentStyle("ConnectionPanelWire").GetDefaultSprite();
96  connectionSprite = GUIStyle.GetComponentStyle("ConnectionPanelConnection").GetDefaultSprite();
97  connectionSpriteHighlight = GUIStyle.GetComponentStyle("ConnectionPanelConnection").GetSprite(GUIComponent.ComponentState.Hover);
98  screwSprites = GUIStyle.GetComponentStyle("ConnectionPanelScrew").Sprites[GUIComponent.ComponentState.None].Select(s => s.Sprite).ToList();
99  }
100 #endif
101  ConnectionPanel = connectionPanel;
102  item = connectionPanel.Item;
103 
104  MaxWires = element.GetAttributeInt("maxwires", DefaultMaxWires);
105  MaxWires = Math.Max(element.Elements().Count(e => e.Name.ToString().Equals("link", StringComparison.OrdinalIgnoreCase)), MaxWires);
106 
107  MaxPlayerConnectableWires = element.GetAttributeInt("maxplayerconnectablewires", MaxWires);
108  wires = new HashSet<Wire>();
109 
110  IsOutput = element.Name.ToString() == "output";
111  Name = element.GetAttributeString("name", IsOutput ? "output" : "input");
112 
113  string displayNameTag = "", fallbackTag = "";
114  //if displayname is not present, attempt to find it from the prefab
115  if (element.GetAttribute("displayname") == null)
116  {
117  foreach (var subElement in item.Prefab.ConfigElement.Elements())
118  {
119  if (!subElement.Name.ToString().Equals("connectionpanel", StringComparison.OrdinalIgnoreCase)) { continue; }
120 
121  foreach (XElement connectionElement in subElement.Elements())
122  {
123  string prefabConnectionName = connectionElement.GetAttributeString("name", null);
124  string[] aliases = connectionElement.GetAttributeStringArray("aliases", Array.Empty<string>());
125  if (prefabConnectionName == Name || aliases.Contains(Name))
126  {
127  displayNameTag = connectionElement.GetAttributeString("displayname", "");
128  fallbackTag = connectionElement.GetAttributeString("fallbackdisplayname", "");
129  }
130  }
131  }
132  }
133  else
134  {
135  displayNameTag = element.GetAttributeString("displayname", "");
136  fallbackTag = element.GetAttributeString("fallbackdisplayname", null);
137  }
138 
139  if (!string.IsNullOrEmpty(displayNameTag))
140  {
141  //extract the tag parts in case the tags contains variables
142  string tagWithoutVariables = displayNameTag?.Split('~')?.FirstOrDefault();
143  string fallbackTagWithoutVariables = fallbackTag?.Split('~')?.FirstOrDefault();
144  //use displayNameTag if found, otherwise fallBack
145  if (TextManager.ContainsTag(tagWithoutVariables))
146  {
147  DisplayName = TextManager.GetServerMessage(displayNameTag);
148  }
149  else if (TextManager.ContainsTag(fallbackTagWithoutVariables))
150  {
151  DisplayName = TextManager.GetServerMessage(fallbackTag);
152  }
153  }
154 
155  if (DisplayName.IsNullOrEmpty())
156  {
157 #if DEBUG
158  DebugConsole.ThrowError("Missing display name in connection " + item.Name + ": " + Name);
159 #endif
160  DisplayName = Name;
161  }
162 
163  IsPower = Name == "power_in" || Name == "power" || Name == "power_out";
164 
165 
166  LoadedWires = new List<(ushort wireId, int? connectionIndex)>();
167  foreach (var subElement in element.Elements())
168  {
169  switch (subElement.Name.ToString().ToLowerInvariant())
170  {
171  case "link":
172  int id = subElement.GetAttributeInt("w", 0);
173  int? i = null;
174  if (subElement.GetAttribute("i") != null)
175  {
176  i = subElement.GetAttributeInt("i", 0);
177  }
178  if (id < 0) { id = 0; }
179  if (LoadedWires.Count < MaxWires) { LoadedWires.Add((idRemap.GetOffsetId(id), i)); }
180  break;
181  case "statuseffect":
182  Effects ??= new List<StatusEffect>();
183  Effects.Add(StatusEffect.Load(subElement, item.Name + ", connection " + Name));
184  break;
185  }
186  }
187  }
188 
193  => wires.Count > 0 || CircuitBoxConnections.Count > 0;
194 
195  public void SetRecipientsDirty()
196  {
197  recipientsDirty = true;
198  if (IsPower) { Powered.ChangedConnections.Add(this); }
199  }
200 
201  private void RefreshRecipients()
202  {
203  recipients.Clear();
204  foreach (var wire in wires)
205  {
206  Connection recipient = wire.OtherConnection(this);
207  if (recipient != null) { recipients.Add(recipient); }
208  }
209  recipientsDirty = false;
210  }
211 
213  => Wires.FirstOrDefault(w => w.Item == it);
214 
215  public bool WireSlotsAvailable()
216  => wires.Count < MaxWires;
217 
218  public bool TryAddLink(Wire wire)
219  {
220  if (wire is null
221  || wires.Contains(wire)
222  || !WireSlotsAvailable())
223  {
224  return false;
225  }
226  wires.Add(wire);
227  return true;
228  }
229 
230  public void DisconnectWire(Wire wire)
231  {
232  if (wire == null || !wires.Contains(wire)) { return; }
233 
234  var prevOtherConnection = wire.OtherConnection(this);
235  if (prevOtherConnection != null)
236  {
237  //Change the connection grids or flag them for updating
238  if (IsPower && prevOtherConnection.IsPower && Grid != null)
239  {
240  //Check if both connections belong to a larger grid
241  if (prevOtherConnection.recipients.Count > 1 && recipients.Count > 1)
242  {
243  Powered.ChangedConnections.Add(prevOtherConnection);
244  Powered.ChangedConnections.Add(this);
245  }
246  else if (recipients.Count > 1)
247  {
248  //This wire was the only one at the other grid
249  prevOtherConnection.Grid?.RemoveConnection(prevOtherConnection);
250  prevOtherConnection.Grid = null;
251  }
252  else if (prevOtherConnection.recipients.Count > 1)
253  {
254  Grid?.RemoveConnection(this);
255  Grid = null;
256  }
257  else if (Grid.Connections.Count == 2)
258  {
259  //Delete the grid as these were the only 2 devices
260  Powered.Grids.Remove(Grid.ID);
261  Grid = null;
262  prevOtherConnection.Grid = null;
263  }
264  }
265  prevOtherConnection.recipientsDirty = true;
266  }
267  if (enumeratingWires)
268  {
269  removedWires.Add(wire);
270  }
271  else
272  {
273  wires.Remove(wire);
274  }
275  recipientsDirty = true;
276  }
277 
278  public void ConnectWire(Wire wire)
279  {
280  if (wire == null || !TryAddLink(wire)) { return; }
281  ConnectionPanel.DisconnectedWires.Remove(wire);
282  var otherConnection = wire.OtherConnection(this);
283  if (otherConnection != null)
284  {
285  //Set the other connection grid if a grid exists already
286  if (Powered.ValidPowerConnection(this, otherConnection))
287  {
288  if (Grid == null && otherConnection.Grid != null)
289  {
290  otherConnection.Grid.AddConnection(this);
291  Grid = otherConnection.Grid;
292  }
293  else if (Grid != null && otherConnection.Grid == null)
294  {
295  Grid.AddConnection(otherConnection);
296  otherConnection.Grid = Grid;
297  }
298  else
299  {
300  //Flag change so that proper grids can be formed
301  Powered.ChangedConnections.Add(this);
302  Powered.ChangedConnections.Add(otherConnection);
303  }
304  }
305 
306  otherConnection.recipientsDirty = true;
307  }
308  recipientsDirty = true;
309  }
310 
311  public void SendSignal(Signal signal)
312  {
313  LastSentSignal = signal;
314  enumeratingWires = true;
315  foreach (var wire in wires)
316  {
317  Connection recipient = wire.OtherConnection(this);
318  if (recipient == null) { continue; }
319  if (recipient.item == this.item || signal.source?.LastSentSignalRecipients.LastOrDefault() == recipient) { continue; }
320 
321  signal.source?.LastSentSignalRecipients.Add(recipient);
322 #if CLIENT
323  wire.RegisterSignal(signal, source: this);
324 #endif
325  SendSignalIntoConnection(signal, recipient);
326  GameMain.LuaCs.Hook.Call("signalReceived", signal, recipient);
327  GameMain.LuaCs.Hook.Call("signalReceived." + recipient.item.Prefab.Identifier, signal, recipient);
328  }
329 
330  foreach (CircuitBoxConnection connection in CircuitBoxConnections)
331  {
332  connection.ReceiveSignal(signal);
333  GameMain.LuaCs.Hook.Call("signalReceived", signal, connection.Connection);
334  GameMain.LuaCs.Hook.Call("signalReceived." + connection.Connection.Item.Prefab.Identifier, signal, connection);
335  }
336  enumeratingWires = false;
337  foreach (var removedWire in removedWires)
338  {
339  wires.Remove(removedWire);
340  }
341  removedWires.Clear();
342  }
343 
344  public static void SendSignalIntoConnection(Signal signal, Connection conn)
345  {
346  conn.LastReceivedSignal = signal;
347 
348  foreach (ItemComponent ic in conn.item.Components)
349  {
350  ic.ReceiveSignal(signal, conn);
351  }
352 
353  if (conn.Effects == null || signal.value == "0") { return; }
354 
355  foreach (StatusEffect effect in conn.Effects)
356  {
357  conn.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step);
358  }
359  }
360 
361  public void ClearConnections()
362  {
363  if (IsPower && Grid != null)
364  {
365  Powered.ChangedConnections.Add(this);
366  foreach (Connection c in recipients)
367  {
369  }
370  }
371  foreach (var wire in wires)
372  {
373  wire.RemoveConnection(this);
374  recipientsDirty = true;
375  }
376 
377  if (enumeratingWires)
378  {
379  foreach (var wire in wires)
380  {
381  removedWires.Add(wire);
382  }
383  }
384  else
385  {
386  wires.Clear();
387  }
388  }
389 
390  public void InitializeFromLoaded()
391  {
392  if (LoadedWires.Count == 0) { return; }
393 
394  foreach ((ushort wireId, int? connectionIndex) in LoadedWires)
395  {
396  if (Entity.FindEntityByID(wireId) is not Item wireItem) { continue; }
397 
398  var wire = wireItem.GetComponent<Wire>();
399  if (wire != null && TryAddLink(wire))
400  {
401  if (wire.Item.body != null) { wire.Item.body.Enabled = false; }
402  if (connectionIndex.HasValue)
403  {
404  wire.Connect(this, connectionIndex.Value, addNode: false, sendNetworkEvent: false);
405  }
406  else
407  {
408  wire.TryConnect(this, addNode: false, sendNetworkEvent: false);
409  }
410  wire.FixNodeEnds();
411  recipientsDirty = true;
412  }
413  }
414  LoadedWires.Clear();
415  }
416 
417 
418  public void Save(XElement parentElement)
419  {
420  XElement newElement = new XElement(IsOutput ? "output" : "input", new XAttribute("name", Name));
421 
422  foreach (var wire in wires.OrderBy(w => w.Item.ID))
423  {
424  newElement.Add(new XElement("link",
425  new XAttribute("w", wire.Item.ID.ToString()),
426  new XAttribute("i", wire.Connections[0] == this ? 0 : 1)));
427  }
428 
429  parentElement.Add(newElement);
430  }
431  }
432 }
string? GetAttributeString(string key, string? def)
IEnumerable< ContentXElement > Elements()
int GetAttributeInt(string key, int def)
XAttribute? GetAttribute(string name)
static Entity FindEntityByID(ushort ID)
Find an entity based on the ID
Definition: Entity.cs:204
static LuaCsSetup LuaCs
Definition: GameMain.cs:26
bool IsConnectedToSomething()
Checks if the the connection is connected to a wire or a circuit box connection
List< CircuitBoxConnection > CircuitBoxConnections
Circuit box input and output connections that are linked to this connection.
Connection(ContentXElement element, ConnectionPanel connectionPanel, IdRemap idRemap)
readonly HashSet< Wire > DisconnectedWires
Wires that have been disconnected from the panel, but not removed completely (visible at the bottom o...
The base class for components holding the different functionalities of the item
object Call(string name, params object[] args)
readonly Identifier Identifier
Definition: Prefab.cs:34
StatusEffects can be used to execute various kinds of effects: modifying the state of some entity in ...
static StatusEffect Load(ContentXElement element, string parentDebugName)
PowerPriority
Order in which power sources will provide to a grid, lower number is higher priority
ActionType
ActionTypes define when a StatusEffect is executed.
Definition: Enums.cs:19