Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs
2 using Microsoft.Xna.Framework;
3 using System;
4 using System.Collections.Generic;
5 using System.Globalization;
6 using System.Linq;
7 using System.Xml.Linq;
8 #if CLIENT
9 using Microsoft.Xna.Framework.Input;
10 #endif
11 
13 {
15  {
16  public partial class WireSection
17  {
18  private Vector2 start;
19  private Vector2 end;
20 
21  private readonly float angle;
22  public readonly float Length;
23 
24  public Vector2 Start
25  {
26  get { return start; }
27  }
28  public Vector2 End
29  {
30  get { return end; }
31  }
32 
33  public WireSection(Vector2 start, Vector2 end)
34  {
35  this.start = start;
36  this.end = end;
37 
38  angle = MathUtils.VectorToAngle(end - start);
39  Length = Vector2.Distance(start, end);
40  }
41  }
42 
43  private bool shouldClearConnections = true;
44 
45  const float MaxAttachDistance = 150.0f;
46 
47  const float MinNodeDistance = 7.0f;
48 
49  const int MaxNodeCount = 255;
50  const int MaxNodesPerNetworkEvent = 30;
51 
52  private List<Vector2> nodes;
53  private readonly List<WireSection> sections;
54 
55  private readonly Connection[] connections;
56 
57  private bool canPlaceNode;
58  private Vector2 newNodePos;
59 
60  private Vector2 sectionExtents;
61 
62  private float currLength;
63 
64  public bool Hidden;
65 
66  private float editNodeDelay;
67 
68  private bool locked;
69  public bool Locked
70  {
71  get
72  {
73  if (GameMain.NetworkMember?.ServerSettings != null && !GameMain.NetworkMember.ServerSettings.AllowRewiring) { return false; }
74  return locked || connections.Any(c => c != null && (c.ConnectionPanel.Locked || c.ConnectionPanel.TemporarilyLocked));
75  }
76  set { locked = value; }
77  }
78 
80  {
81  get { return connections; }
82  }
83 
84  public float Length { get; private set; }
85 
86  [Serialize(5000.0f, IsPropertySaveable.No, description: "The maximum distance the wire can extend (in pixels).")]
87  public float MaxLength
88  {
89  get;
90  set;
91  }
92 
93  [Serialize(false, IsPropertySaveable.No, description: "If enabled, the wire will not be visible in connection panels outside the submarine editor.")]
94  public bool HiddenInGame
95  {
96  get;
97  set;
98  }
99 
100  [Editable, Serialize(false, IsPropertySaveable.Yes, "If enabled, this wire will be ignored by the \"Lock all default wires\" setting.", alwaysUseInstanceValues: true)]
101  public bool NoAutoLock
102  {
103  get;
104  set;
105  }
106 
107  [Editable, Serialize(false, IsPropertySaveable.Yes, "If enabled, this wire will use the sprite depth instead of a constant depth.")]
108  public bool UseSpriteDepth
109  {
110  get;
111  set;
112  }
113 
114  [Serialize(true, IsPropertySaveable.Yes, "If disabled, the wire will not be dropped when connecting. Used in circuit box to store the wires inside the box.")]
115  public bool DropOnConnect
116  {
117  get;
118  set;
119  }
120 
121  public Wire(Item item, ContentXElement element)
122  : base(item, element)
123  {
124  nodes = new List<Vector2>();
125  sections = new List<WireSection>();
126  connections = new Connection[2];
127  IsActive = false;
128  item.IsShootable = true;
129 
130  InitProjSpecific(element);
131  }
132 
133  partial void InitProjSpecific(ContentXElement element);
134 
136  {
137  if (connection == connections[0]) { return connections[1]; }
138  if (connection == connections[1]) { return connections[0]; }
139 
140  return null;
141  }
142 
143  public bool IsConnectedTo(Item item)
144  {
145  if (connections[0] != null && connections[0].Item == item) { return true; }
146  return connections[1] != null && connections[1].Item == item;
147  }
148 
150  {
151  for (int i = 0; i < 2; i++)
152  {
153  if (connections[i] == null || connections[i].Item != item) { continue; }
154 
155  if (connections[i].Wires.Contains(this))
156  {
158 
159  connections[i].DisconnectWire(this);
160  }
161 
162  connections[i] = null;
163  }
164  }
165 
166  public void RemoveConnection(Connection connection)
167  {
168  if (connection == connections[0]) { connections[0] = null; }
169  if (connection == connections[1]) { connections[1] = null; }
170 
172  }
173 
178 
179  public bool TryConnect(Connection newConnection, bool addNode = true, bool sendNetworkEvent = false)
180  {
181  if (connections[0] == null)
182  {
183  return Connect(newConnection, 0, addNode, sendNetworkEvent);
184  }
185  else if (connections[1] == null)
186  {
187  return Connect(newConnection, 1, addNode, sendNetworkEvent);
188  }
189  return false;
190  }
191 
192 
201  public bool Connect(Connection newConnection, int connectionIndex, bool addNode = true, bool sendNetworkEvent = false)
202  {
203  for (int i = 0; i < 2; i++)
204  {
205  if (connections[i] == newConnection) { return false; }
206  }
207 
208  if (connectionIndex < 0 || connectionIndex > 1)
209  {
210  DebugConsole.ThrowError($"Error while connecting a wire to {newConnection.Item}: {connectionIndex} is not a valid index.");
211  return false;
212  }
213  if (connections[connectionIndex] != null)
214  {
215  DebugConsole.ThrowError($"Error while connecting a wire to {newConnection.Item}: a wire is already connected to the index {connectionIndex}.");
216  return false;
217  }
218 
219  for (int i = 0; i < 2; i++)
220  {
221  if (connections[i] != null && connections[i].Item == newConnection.Item)
222  {
223  addNode = false;
224  break;
225  }
226  }
227  if (item.body != null) { item.Submarine = newConnection.Item.Submarine; }
228 
229  newConnection.ConnectionPanel.DisconnectedWires.Remove(this);
230 
231  connections[connectionIndex] = newConnection;
232  FixNodeEnds();
233 
234  if (addNode)
235  {
236  AddNode(newConnection, connectionIndex);
237  }
238 
240 
241  if (DropOnConnect)
242  {
243  if (connections[0] != null && connections[1] != null)
244  {
245  foreach (ItemComponent ic in item.Components)
246  {
247  if (ic == this) { continue; }
248 
249  ic.Drop(null);
250  }
251 
253  if (item.body != null) { item.body.Enabled = false; }
254 
255  IsActive = false;
256 
257  CleanNodes();
258  }
259  }
260 
261  if (item.body != null) { item.Submarine = newConnection.Item.Submarine; }
262 
263  if (sendNetworkEvent)
264  {
265 #if SERVER
266  if (GameMain.Server != null)
267  {
268  CreateNetworkEvent();
269  }
270 #endif
271  //the wire is active if it's currently being wired to something (in character inventory and connected from one end)
272  IsActive =
274  connections[0] == null ^ connections[1] == null;
275  }
276 
277  Drawable = IsActive || nodes.Any();
278 
279  UpdateSections();
280  return true;
281  }
282 
283  private void AddNode(Connection newConnection, int selectedIndex)
284  {
285  Submarine refSub = newConnection.Item.Submarine;
286  if (refSub == null)
287  {
288  Structure attachTarget = Structure.GetAttachTarget(newConnection.Item.WorldPosition);
289  if (attachTarget == null && !(newConnection.Item.GetComponent<Holdable>()?.Attached ?? false))
290  {
291  connections[selectedIndex] = null;
292  return;
293  }
294  refSub = attachTarget?.Submarine;
295  }
296 
297  Vector2 nodePos = RoundNode(newConnection.Item.Position);
298  if (refSub != null) { nodePos -= refSub.HiddenSubPosition; }
299 
300  if (nodes.Count > 0 && nodes[0] == nodePos) { return; }
301  if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) { return; }
302 
303  //make sure we place the node at the correct end of the wire (the end that's closest to the new node pos)
304  int newNodeIndex = 0;
305  if (nodes.Count > 1)
306  {
307  if (connections[0] != null && connections[0] != newConnection)
308  {
309  if (Vector2.DistanceSquared(nodes[0], connections[0].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)) <
310  Vector2.DistanceSquared(nodes[nodes.Count - 1], connections[0].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)))
311  {
312  newNodeIndex = nodes.Count;
313  }
314  }
315  else if (connections[1] != null && connections[1] != newConnection)
316  {
317  if (Vector2.DistanceSquared(nodes[0], connections[1].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)) <
318  Vector2.DistanceSquared(nodes[nodes.Count - 1], connections[1].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)))
319  {
320  newNodeIndex = nodes.Count;
321  }
322  }
323  else if (Vector2.DistanceSquared(nodes[nodes.Count - 1], nodePos) < Vector2.DistanceSquared(nodes[0], nodePos))
324  {
325  newNodeIndex = nodes.Count;
326  }
327  }
328 
329  if (newNodeIndex == 0 && nodes.Count > 1)
330  {
331  nodes.Insert(0, nodePos);
332  }
333  else
334  {
335  nodes.Add(nodePos);
336  }
337  }
338 
339  public override void Equip(Character character)
340  {
341  if (shouldClearConnections) { ClearConnections(character); }
342  IsActive = true;
343  }
344 
345  public override void Unequip(Character character)
346  {
347  ClearConnections(character);
348  IsActive = false;
349  }
350 
351  public override void Drop(Character dropper, bool setTransform = true)
352  {
353  if (shouldClearConnections) { ClearConnections(dropper); }
354  IsActive = false;
355  }
356 
357  public override void Update(float deltaTime, Camera cam)
358  {
359  if (nodes.Count == 0) { return; }
360 
362  editNodeDelay = (user?.SelectedItem == null) ? editNodeDelay - deltaTime : 0.5f;
363 
364  Submarine sub = item.Submarine;
365  if (connections[0] != null && connections[0].Item.Submarine != null) { sub = connections[0].Item.Submarine; }
366  if (connections[1] != null && connections[1].Item.Submarine != null) { sub = connections[1].Item.Submarine; }
367 
369  {
370  if (user != null) { NoAutoLock = true; }
371 
372  //cannot run wires from sub to another
373  if (item.Submarine != sub && sub != null && item.Submarine != null)
374  {
376  return;
377  }
378 
379  if (item.CurrentHull == null)
380  {
382  canPlaceNode = attachTarget != null;
383 
384  sub ??= attachTarget?.Submarine;
385  Vector2 attachPos = GetAttachPosition(user);
386  newNodePos = sub == null ?
387  attachPos :
388  attachPos - sub.Position - sub.HiddenSubPosition;
389  }
390  else
391  {
392  newNodePos = GetAttachPosition(user);
393  if (sub != null) { newNodePos -= sub.HiddenSubPosition; }
394  canPlaceNode = true;
395  }
396 
397  //prevent the wire from extending too far when rewiring
398  if (nodes.Count > 0)
399  {
400  if (user == null) { return; }
401 
402  Vector2 prevNodePos = nodes[nodes.Count - 1];
403  if (sub != null) { prevNodePos += sub.HiddenSubPosition; }
404 
405  currLength = 0.0f;
406  for (int i = 0; i < nodes.Count - 1; i++)
407  {
408  currLength += Vector2.Distance(nodes[i], nodes[i + 1]);
409  }
410  Vector2 itemPos = item.Position;
411  if (sub != null && user.Submarine == null) { prevNodePos += sub.Position; }
412  currLength += Vector2.Distance(prevNodePos, itemPos);
413  if (currLength > MaxLength)
414  {
415  Vector2 diff = prevNodePos - user.Position;
416  Vector2 pullBackDir = diff == Vector2.Zero ? Vector2.Zero : Vector2.Normalize(diff);
417  Vector2 forceDir = pullBackDir;
418  if (!user.AnimController.InWater) { forceDir.Y = 0.0f; }
419  user.AnimController.Collider.ApplyForce(forceDir * user.Mass * 50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.5f);
420  if (diff.LengthSquared() > 50.0f * 50.0f)
421  {
422  user.AnimController.UpdateUseItem(!user.IsClimbing, user.WorldPosition + pullBackDir * Math.Min(150.0f, diff.Length()));
423  }
424 
425  if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
426  {
427  if (currLength > MaxLength * 1.5f)
428  {
430 #if SERVER
431  CreateNetworkEvent();
432 #endif
433  return;
434  }
435  }
436  }
437  }
438  }
439  else
440  {
441 #if CLIENT
442  bool disableGrid = SubEditorScreen.IsSubEditor() && PlayerInput.IsShiftDown();
443  newNodePos = disableGrid ? item.Position : RoundNode(item.Position);
444 #else
445  newNodePos = RoundNode(item.Position);
446 #endif
447  if (sub != null) { newNodePos -= sub.HiddenSubPosition; }
448  canPlaceNode = true;
449  }
450 
451  if (item != null)
452  {
453  Vector2 relativeNodePos = newNodePos - item.Position;
454 
455  if (sub != null)
456  {
457  relativeNodePos += sub.HiddenSubPosition;
458  }
459 
460  sectionExtents = new Vector2(
461  Math.Max(Math.Abs(relativeNodePos.X), sectionExtents.X),
462  Math.Max(Math.Abs(relativeNodePos.Y), sectionExtents.Y));
463  }
464  }
465 
466  private Vector2 GetAttachPosition(Character user)
467  {
468  if (user == null) { return item.Position; }
469 
470  Vector2 mouseDiff = user.CursorWorldPosition - user.WorldPosition;
471  mouseDiff = mouseDiff.ClampLength(MaxAttachDistance);
472 
473  return RoundNode(user.Position + mouseDiff);
474  }
475 
476  public override bool Use(float deltaTime, Character character = null)
477  {
478  if (character == null || character != Character.Controlled) { return false; }
479  if (character.HasSelectedAnyItem) { return false; }
480 #if CLIENT
482  {
483  return false;
484  }
485 #endif
486  //clients communicate node addition/removal with network events
487  if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { return false; }
488 
489  if (newNodePos != Vector2.Zero && canPlaceNode && editNodeDelay <= 0.0f && nodes.Count > 0 &&
490  Vector2.DistanceSquared(newNodePos, nodes[nodes.Count - 1]) > MinNodeDistance * MinNodeDistance)
491  {
492  if (nodes.Count >= MaxNodeCount)
493  {
494  nodes.RemoveAt(nodes.Count - 1);
495  }
496 
497  nodes.Add(newNodePos);
498  CleanNodes();
499  UpdateSections();
500  Drawable = true;
501  newNodePos = Vector2.Zero;
502 #if CLIENT
503  if (GameMain.NetworkMember != null)
504  {
505  item.CreateClientEvent(this, new ClientEventData(nodes.Count));
506  }
507 #endif
508  }
509  editNodeDelay = 0.1f;
510  return true;
511  }
512 
513  public override bool SecondaryUse(float deltaTime, Character character = null)
514  {
515  if (character == null || character != Character.Controlled) { return false; }
516 
517  //clients communicate node addition/removal with network events
518  if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { return false; }
519 
520  if (nodes.Count > 1 && editNodeDelay <= 0.0f)
521  {
522  nodes.RemoveAt(nodes.Count - 1);
523  UpdateSections();
524 #if CLIENT
525  if (GameMain.NetworkMember != null)
526  {
527  item.CreateClientEvent(this, new ClientEventData(nodes.Count));
528  }
529 #endif
530  }
531  editNodeDelay = 0.1f;
532 
533  Drawable = IsActive || sections.Count > 0;
534  return true;
535  }
536 
537  public override bool Pick(Character picker)
538  {
539  ClearConnections(picker);
540  return true;
541  }
542 
543  public List<Vector2> GetNodes()
544  {
545  return new List<Vector2>(nodes);
546  }
547 
548  public void SetNodes(List<Vector2> nodes)
549  {
550  this.nodes = new List<Vector2>(nodes);
551  UpdateSections();
552  }
553 
554  public void MoveNode(int index, Vector2 amount)
555  {
556  if (index < 0 || index >= nodes.Count) return;
557  nodes[index] += amount;
558  UpdateSections();
559  }
560 
561  public void MoveNodes(Vector2 amount)
562  {
563  for (int i = 0; i < nodes.Count; i++)
564  {
565  nodes[i] += amount;
566  }
567  UpdateSections();
568  }
569 
570  public void UpdateSections()
571  {
572  sections.Clear();
573 
574  for (int i = 0; i < nodes.Count - 1; i++)
575  {
576  sections.Add(new WireSection(nodes[i], nodes[i + 1]));
577  }
578  Drawable = IsActive || sections.Count > 0;
579  Length = sections.Count > 0 ? sections.Sum(s => s.Length) : 0;
580  CalculateExtents();
581  }
582 
583  private void CalculateExtents()
584  {
585  sectionExtents = Vector2.Zero;
586  if (sections.Count > 0)
587  {
588  for (int i = 0; i < nodes.Count; i++)
589  {
590  sectionExtents.X = Math.Max(Math.Abs(nodes[i].X - item.Position.X), sectionExtents.X);
591  sectionExtents.Y = Math.Max(Math.Abs(nodes[i].Y - item.Position.Y), sectionExtents.Y);
592  }
593  }
594 #if CLIENT
596 #endif
597  }
598 
599  public void ClearConnections(Character user = null)
600  {
601  nodes.Clear();
602  sections.Clear();
603 
604  foreach (Item item in Item.ItemList)
605  {
606  var connectionPanel = item.GetComponent<ConnectionPanel>();
607  if (connectionPanel != null && connectionPanel.DisconnectedWires.Contains(this) && !item.Removed)
608  {
609 #if SERVER
610  item.CreateServerEvent(connectionPanel);
611 #endif
612  connectionPanel.DisconnectedWires.Remove(this);
613  }
614  }
615 
616 #if SERVER
617  if (user != null)
618  {
619  if (connections[0] != null || connections[1] != null)
620  {
621  GameMain.Server.KarmaManager.OnWireDisconnected(user, this);
622  }
623 
624  if (connections[0] != null && connections[1] != null)
625  {
626  GameServer.Log(GameServer.CharacterLogName(user) + " disconnected a wire from " +
627  connections[0].Item.Name + " (" + connections[0].Name + ") to "+
628  connections[1].Item.Name + " (" + connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
629  }
630  else if (connections[0] != null)
631  {
632  GameServer.Log(GameServer.CharacterLogName(user) + " disconnected a wire from " +
633  connections[0].Item.Name + " (" + connections[0].Name + ")", ServerLog.MessageType.ItemInteraction);
634  }
635  else if (connections[1] != null)
636  {
637  GameServer.Log(GameServer.CharacterLogName(user) + " disconnected a wire from " +
638  connections[1].Item.Name + " (" + connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
639  }
640  }
641 #endif
642 
644 
645  for (int i = 0; i < 2; i++)
646  {
647  if (connections[i] == null) { continue; }
648 
649  var wire = connections[i].FindWireByItem(item);
650  if (wire is null) { continue; }
651 #if SERVER
652  if (!connections[i].Item.Removed && (!connections[i].Item.Submarine?.Loading ?? true) && (!Level.Loaded?.Generating ?? true))
653  {
654  connections[i].Item.CreateServerEvent(connections[i].Item.GetComponent<ConnectionPanel>());
655  }
656 #endif
657  connections[i].DisconnectWire(wire);
658  connections[i] = null;
659  }
660 
661  Drawable = sections.Count > 0;
662  }
663 
664  private static Vector2 RoundNode(Vector2 position)
665  {
666  Vector2 halfGrid = Submarine.GridSize / 2;
667 
668  position += halfGrid;
669  position.X = MathUtils.RoundTowardsClosest(position.X, Submarine.GridSize.X / 2.0f);
670  position.Y = MathUtils.RoundTowardsClosest(position.Y, Submarine.GridSize.Y / 2.0f);
671  return position - halfGrid;
672  }
673 
674  public void SetConnectedDirty()
675  {
676  for (int i = 0; i < 2; i++)
677  {
678  if (connections[i]?.Item != null)
679  {
680  connections[i].Item.GetComponent<PowerTransfer>()?.SetConnectionDirty(connections[i]);
681  connections[i].SetRecipientsDirty();
682  }
683  }
684  }
685 
686  private void CleanNodes()
687  {
688  bool removed;
689  do
690  {
691  removed = false;
692  for (int i = nodes.Count - 2; i > 0; i--)
693  {
694  if (Math.Abs(nodes[i - 1].X - nodes[i].X) < 1.0f && Math.Abs(nodes[i + 1].X - nodes[i].X) < 1.0f &&
695  Math.Sign(nodes[i - 1].Y - nodes[i].Y) != Math.Sign(nodes[i + 1].Y - nodes[i].Y))
696  {
697  nodes.RemoveAt(i);
698  removed = true;
699  }
700  else if (Math.Abs(nodes[i - 1].Y - nodes[i].Y) < 1.0f && Math.Abs(nodes[i + 1].Y - nodes[i].Y) < 1.0f &&
701  Math.Sign(nodes[i - 1].X - nodes[i].X) != Math.Sign(nodes[i + 1].X - nodes[i].X))
702  {
703  nodes.RemoveAt(i);
704  removed = true;
705  }
706  }
707 
708  } while (removed);
709  }
710 
711  public void FixNodeEnds()
712  {
713  Item item0 = connections[0]?.Item;
714  Item item1 = connections[1]?.Item;
715 
716  if (item0 == null && item1 != null)
717  {
718  item0 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
719  }
720  else if (item0 != null && item1 == null)
721  {
722  item1 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
723  }
724 
725  if (item0 == null || item1 == null || nodes.Count == 0) { return; }
726 
727  Vector2 nodePos = nodes[0];
728 
729  Submarine refSub = item0.Submarine ?? item1.Submarine;
730  if (refSub != null) { nodePos += refSub.HiddenSubPosition; }
731 
732  float dist1 = Vector2.DistanceSquared(item0.Position, nodePos);
733  float dist2 = Vector2.DistanceSquared(item1.Position, nodePos);
734 
735  //first node is closer to the second item
736  //= the nodes are "backwards", need to reverse them
737  if (dist1 > dist2)
738  {
739  nodes.Reverse();
740  UpdateSections();
741  }
742  }
743 
744  private int GetClosestNodeIndex(Vector2 pos, float maxDist, out float closestDist)
745  {
746  closestDist = 0.0f;
747  int closestIndex = -1;
748 
749  for (int i = 0; i < nodes.Count; i++)
750  {
751  float dist = Vector2.Distance(nodes[i], pos);
752  if (dist > maxDist) continue;
753 
754  if (closestIndex == -1 || dist < closestDist)
755  {
756  closestIndex = i;
757  closestDist = dist;
758  }
759  }
760 
761  return closestIndex;
762  }
763 
764  private int GetClosestSectionIndex(Vector2 mousePos, float maxDist, out float closestDist)
765  {
766  closestDist = 0.0f;
767  int closestIndex = -1;
768 
769  maxDist *= maxDist;
770  for (int i = 0; i < nodes.Count-1; i++)
771  {
772  if ((Math.Abs(nodes[i].X - nodes[i + 1].X)<5 || Math.Sign(mousePos.X - nodes[i].X) != Math.Sign(mousePos.X - nodes[i + 1].X)) &&
773  (Math.Abs(nodes[i].Y - nodes[i + 1].Y)<5 || Math.Sign(mousePos.Y - nodes[i].Y) != Math.Sign(mousePos.Y - nodes[i + 1].Y)))
774  {
775  float dist = MathUtils.LineToPointDistanceSquared(nodes[i], nodes[i + 1], mousePos);
776  if (dist > maxDist) continue;
777 
778  if (closestIndex == -1 || dist < closestDist)
779  {
780  closestIndex = i;
781  closestDist = dist;
782  }
783  }
784  }
785  closestDist = (float)Math.Sqrt(closestDist);
786 
787  return closestIndex;
788  }
789 
790  public override void FlipX(bool relativeToSub)
791  {
792  if (item.ParentInventory != null) { return; }
793 #if CLIENT
794  if (!relativeToSub)
795  {
796  if (Screen.Selected != GameMain.SubEditorScreen || (item.Submarine?.Loading ?? false)) { return; }
797  }
798 #else
799  if (!relativeToSub) { return; }
800 #endif
801 
802  Vector2 refPos = item.Submarine == null ?
803  Vector2.Zero :
805 
806  for (int i = 0; i < nodes.Count; i++)
807  {
808  nodes[i] = relativeToSub ?
809  new Vector2(-nodes[i].X, nodes[i].Y) :
810  new Vector2(refPos.X - (nodes[i].X - refPos.X), nodes[i].Y);
811  }
812  UpdateSections();
813  }
814 
815  public override void FlipY(bool relativeToSub)
816  {
817  Vector2 refPos = item.Submarine == null ?
818  Vector2.Zero :
820 
821  for (int i = 0; i < nodes.Count; i++)
822  {
823  nodes[i] = relativeToSub ?
824  new Vector2(nodes[i].X, -nodes[i].Y) :
825  new Vector2(nodes[i].X, refPos.Y - (nodes[i].Y - refPos.Y));
826  }
827  UpdateSections();
828  }
829 
830  public static IEnumerable<Vector2> ExtractNodes(XElement element)
831  {
832  string nodeString = element.GetAttributeString("nodes", "");
833  if (nodeString.IsNullOrWhiteSpace()) { yield break; }
834 
835  string[] nodeCoords = nodeString.Split(';');
836  for (int i = 0; i < nodeCoords.Length / 2; i++)
837  {
838  float.TryParse(nodeCoords[i * 2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out float x);
839  float.TryParse(nodeCoords[i * 2 + 1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out float y);
840  yield return new Vector2(x, y);
841  }
842  }
843 
844  public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap, bool isItemSwap)
845  {
846  base.Load(componentElement, usePrefabValues, idRemap, isItemSwap);
847 
848  nodes.AddRange(ExtractNodes(componentElement));
849 
850  Drawable = nodes.Any();
851  }
852 
853  public override XElement Save(XElement parentElement)
854  {
855  XElement componentElement = base.Save(parentElement);
856 
857  if (nodes == null || nodes.Count == 0) return componentElement;
858 
859  string[] nodeCoords = new string[nodes.Count * 2];
860  for (int i = 0; i < nodes.Count; i++)
861  {
862  nodeCoords[i * 2] = nodes[i].X.ToString(CultureInfo.InvariantCulture);
863  nodeCoords[i * 2 + 1] = nodes[i].Y.ToString(CultureInfo.InvariantCulture);
864  }
865 
866  componentElement.Add(new XAttribute("nodes", string.Join(";", nodeCoords)));
867 
868  return componentElement;
869  }
870 
871  protected override void ShallowRemoveComponentSpecific()
872  {
873  /*for (int i = 0; i < 2; i++)
874  {
875  if (connections[i] == null) continue;
876  int wireIndex = connections[i].FindWireIndex(item);
877 
878  if (wireIndex > -1)
879  {
880  connections[i].AddLink(wireIndex, null);
881  }
882  }*/
883  }
884 
885  protected override void RemoveComponentSpecific()
886  {
888  base.RemoveComponentSpecific();
889 #if CLIENT
890  if (DraggingWire == this) { draggingWire = null; }
891  overrideSprite?.Remove();
892  overrideSprite = null;
893  wireSprite = null;
894 #endif
895  }
896  }
897 }
void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
virtual Vector2 WorldPosition
Definition: Entity.cs:49
Submarine Submarine
Definition: Entity.cs:53
static SubEditorScreen SubEditorScreen
Definition: GameMain.cs:68
static NetworkMember NetworkMember
Definition: GameMain.cs:190
bool IsShootable
Should the item's Use method be called with the "Use" or with the "Shoot" key?
static readonly List< Item > ItemList
Item(ItemPrefab itemPrefab, Vector2 position, Submarine submarine, ushort id=Entity.NullEntityID, bool callOnItemLoaded=true)
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
virtual void Drop(Character dropper, bool setTransform=true)
a Character has dropped the item
override void Drop(Character dropper, bool setTransform=true)
a Character has dropped the item
bool Connect(Connection newConnection, int connectionIndex, bool addNode=true, bool sendNetworkEvent=false)
Tries to add the given connection to this wire. Note that this only affects the wire - adding the wir...
override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap, bool isItemSwap)
bool TryConnect(Connection newConnection, bool addNode=true, bool sendNetworkEvent=false)
Tries to add the given connection to this wire. Note that this only affects the wire - adding the wir...
override bool SecondaryUse(float deltaTime, Character character=null)
override bool Pick(Character picker)
a Character has picked the item
override bool Use(float deltaTime, Character character=null)
void ApplyForce(Vector2 force, float maxVelocity=NetConfig.MaxPhysicsBodyVelocity)
static Structure GetAttachTarget(Vector2 worldPosition)
Checks if there's a structure items can be attached to at the given position and returns it.
Submarine(SubmarineInfo info, bool showErrorMessages=true, Func< Submarine, List< MapEntity >> loadEntities=null, IdRemap linkedRemap=null)
Interface for entities that the clients can send events to the server
Interface for entities that the server can send events to the clients