Server LuaCsForBarotrauma
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($"Could not find a display name for the connection {Name} in the item {item.Name} (submarine: {item.Submarine?.Info?.Name ?? "none"})");
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:37
List< Connection > LastSentSignalRecipients
A list of connections the last signal sent by this item went through
static void SendSignalIntoConnection(Signal signal, Connection conn)
Definition: Connection.cs:344
bool IsConnectedToSomething()
Checks if the the connection is connected to a wire or a circuit box connection
IReadOnlyCollection< Wire > Wires
Definition: Connection.cs:24
readonly List< StatusEffect > Effects
Definition: Connection.cs:42
readonly List<(ushort wireId, int? connectionIndex)> LoadedWires
Definition: Connection.cs:44
readonly LocalizedString DisplayName
Definition: Connection.cs:21
List< CircuitBoxConnection > CircuitBoxConnections
Circuit box input and output connections that are linked to this connection.
Definition: Connection.cs:33
void Save(XElement parentElement)
Definition: Connection.cs:418
Connection(ContentXElement element, ConnectionPanel connectionPanel, IdRemap idRemap)
Definition: Connection.cs:88
readonly HashSet< Wire > DisconnectedWires
Wires that have been disconnected from the panel, but not removed completely (visible at the bottom o...
void RemoveConnection(Connection c)
Definition: Powered.cs:748
void AddConnection(Connection c)
Definition: Powered.cs:759
readonly List< Connection > Connections
Definition: Powered.cs:740
The base class for components holding the different functionalities of the item
static readonly Dictionary< int, GridInfo > Grids
Definition: Powered.cs:74
static readonly HashSet< Connection > ChangedConnections
Definition: Powered.cs:72
static bool ValidPowerConnection(Connection conn1, Connection conn2)
Definition: Powered.cs:668
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 ...
Definition: StatusEffect.cs:72
static StatusEffect Load(ContentXElement element, string parentDebugName)
PowerPriority
Order in which power sources will provide to a grid, lower number is higher priority
Definition: Powered.cs:15
ActionType
ActionTypes define when a StatusEffect is executed.
Definition: Enums.cs:26