Client LuaCsForBarotrauma
BarotraumaClient/ClientSource/Items/Components/Signal/Wire.cs
2 using Microsoft.Xna.Framework;
3 using Microsoft.Xna.Framework.Graphics;
4 using Microsoft.Xna.Framework.Input;
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 
10 {
11  partial class Wire : ItemComponent, IDrawableComponent, IServerSerializable, IClientSerializable
12  {
13  private readonly struct ClientEventData : IEventData
14  {
15  public readonly int NodeCount;
16 
17  public ClientEventData(int nodeCount)
18  {
19  NodeCount = nodeCount;
20  }
21  }
22 
23  public static Color higlightColor = Color.LightGreen;
24  public static Color editorHighlightColor = Color.Yellow;
25  public static Color editorSelectedColor = Color.Red;
26 
27  public partial class WireSection
28  {
29  public VertexPositionColorTexture[] vertices;
30  public VertexPositionColorTexture[] shiftedVertices;
31 
32  private float cachedWidth = 0f;
33 
34  private void RecalculateVertices(Sprite wireSprite, float width)
35  {
36  if (MathUtils.NearlyEqual(cachedWidth, width)) { return; }
37  cachedWidth = width;
38 
39  vertices = new VertexPositionColorTexture[4];
40 
41  Vector2 expandDir = start-end;
42  expandDir.Normalize();
43  float temp = expandDir.X;
44  expandDir.X = -expandDir.Y;
45  expandDir.Y = -temp;
46 
47  Rectangle srcRect = wireSprite.SourceRect;
48 
49  expandDir *= width * srcRect.Height * 0.5f;
50 
51  Vector2 rectLocation = srcRect.Location.ToVector2();
52  Vector2 rectSize = srcRect.Size.ToVector2();
53  Vector2 textureSize = new Vector2(wireSprite.Texture.Width, wireSprite.Texture.Height);
54 
55  Vector2 topLeftUv = rectLocation / textureSize;
56  Vector2 bottomRightUv = (rectLocation + rectSize) / textureSize;
57 
58  Vector2 invStart = new Vector2(start.X, -start.Y);
59  Vector2 invEnd = new Vector2(end.X, -end.Y);
60 
61  vertices[0] = new VertexPositionColorTexture(new Vector3(invStart + expandDir, 0f), Color.White, topLeftUv);
62  vertices[2] = new VertexPositionColorTexture(new Vector3(invEnd + expandDir, 0f), Color.White, new Vector2(bottomRightUv.X, topLeftUv.Y));
63  vertices[1] = new VertexPositionColorTexture(new Vector3(invStart - expandDir, 0f), Color.White, new Vector2(topLeftUv.X, bottomRightUv.Y));
64  vertices[3] = new VertexPositionColorTexture(new Vector3(invEnd - expandDir, 0f), Color.White, bottomRightUv);
65 
66  shiftedVertices = (VertexPositionColorTexture[])vertices.Clone();
67  }
68 
69  public void Draw(ISpriteBatch spriteBatch, Sprite wireSprite, Color color, Vector2 offset, float depth, float width = 0.3f)
70  {
71  if (width <= 0f) { return; }
72  RecalculateVertices(wireSprite, width);
73 
74  for (int i = 0; i < vertices.Length; i++)
75  {
76  shiftedVertices[i].Color = color;
77  shiftedVertices[i].Position = vertices[i].Position;
78  shiftedVertices[i].Position.X += offset.X;
79  shiftedVertices[i].Position.Y -= offset.Y;
80  }
81  spriteBatch.Draw(
82  wireSprite.Texture,
84  depth);
85  }
86 
87  public static void Draw(ISpriteBatch spriteBatch, Sprite wireSprite, Vector2 start, Vector2 end, Color color, float depth, float width = 0.3f)
88  {
89  start.Y = -start.Y;
90  end.Y = -end.Y;
91 
92  spriteBatch.Draw(wireSprite.Texture,
93  start, wireSprite.SourceRect, color,
94  MathUtils.VectorToAngle(end - start),
95  new Vector2(0.0f, wireSprite.size.Y / 2.0f),
96  new Vector2((Vector2.Distance(start, end)) / wireSprite.size.X, width),
97  SpriteEffects.None,
98  depth);
99  }
100  }
101  private static Sprite defaultWireSprite;
102  private Sprite overrideSprite;
103  private Sprite wireSprite;
104 
105  private static Wire draggingWire;
106  private static int? selectedNodeIndex;
107  private static int? highlightedNodeIndex;
108 
109  [Serialize(0.3f, IsPropertySaveable.No)]
110  public float Width
111  {
112  get;
113  set;
114  }
115 
116  public Vector2 DrawSize
117  {
118  get { return sectionExtents; }
119  }
120 
121  public readonly record struct VisualSignal(
122  float TimeSent,
123  Color Color,
124  int Direction);
125 
126  private VisualSignal lastReceivedSignal;
127 
128  public static Wire DraggingWire
129  {
130  get => draggingWire;
131  }
132 
133  public static Sprite ExtractWireSprite(ContentXElement element)
134  {
135  defaultWireSprite ??=
136  new Sprite("Content/Items/Electricity/signalcomp.png", new Rectangle(970, 47, 14, 16), new Vector2(0.5f, 0.5f))
137  {
138  Depth = 0.855f
139  };
140 
141  Sprite overrideSprite = null;
142  foreach (var subElement in element.Elements())
143  {
144  if (subElement.Name.ToString().Equals("wiresprite", StringComparison.OrdinalIgnoreCase))
145  {
146  overrideSprite = new Sprite(subElement);
147  break;
148  }
149  }
150 
151  return overrideSprite ?? defaultWireSprite;
152  }
153 
154  partial void InitProjSpecific(ContentXElement element)
155  {
156  wireSprite = ExtractWireSprite(element);
157  if (wireSprite != defaultWireSprite) { overrideSprite = wireSprite; }
158  }
159 
160  public void RegisterSignal(Signal signal, Connection source)
161  {
162  lastReceivedSignal = new VisualSignal(
163  (float)Timing.TotalTimeUnpaused,
164  GetSignalColor(signal),
165  Direction: source == connections[0] ? 1 : -1);
166  }
167 
168  private static readonly Color[] dataSignalColors = new Color[] { Color.White, Color.LightBlue, Color.CornflowerBlue, Color.Blue, Color.BlueViolet, Color.Violet };
169 
170  private static Color GetSignalColor(Signal signal)
171  {
172  if (signal.value == "0")
173  {
174  return Color.Red;
175  }
176  else if (signal.value == "1")
177  {
178  return Color.LightGreen;
179  }
180  else if (float.TryParse(signal.value, out float floatValue))
181  {
182  //convert numeric values to a color (guessing the value might be somewhere in the range of 0-200)
183  //so a player with a keen eye can get some info out of the color of the signal
184  return ToolBox.GradientLerp(Math.Abs(floatValue / 200.0f), dataSignalColors);
185  }
186  return Color.LightBlue;
187  }
188 
189  public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
190  {
191  Draw(spriteBatch, editing, Vector2.Zero, itemDepth, overrideColor);
192  }
193 
194  public void Draw(SpriteBatch spriteBatch, bool editing, Vector2 offset, float itemDepth = -1, Color? overrideColor = null)
195  {
196  if (sections.Count == 0 && !IsActive || Hidden)
197  {
198  Drawable = false;
199  return;
200  }
201 
202  Vector2 drawOffset = GetDrawOffset() + offset;
203 
204  float baseDepth = UseSpriteDepth ? item.SpriteDepth : wireSprite.Depth;
205  float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : baseDepth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
206 
207  if (item.IsHighlighted)
208  {
209  foreach (WireSection section in sections)
210  {
211  section.Draw(spriteBatch, wireSprite,
213  drawOffset, depth + 0.00001f, Width * 2.0f);
214  }
215  }
216  else if (item.IsSelected)
217  {
218  foreach (WireSection section in sections)
219  {
220  section.Draw(spriteBatch, wireSprite, editorSelectedColor, drawOffset, depth + 0.00001f, Width * 2.0f);
221  }
222  }
223 
224  foreach (WireSection section in sections)
225  {
226  section.Draw(spriteBatch, wireSprite, overrideColor ?? item.Color, drawOffset, depth, Width);
227  }
228 
229  if (nodes.Count > 0)
230  {
231  if (!IsActive)
232  {
233  if (connections[0] == null) { DrawHangingWire(spriteBatch, nodes[0] + drawOffset, depth); }
234  if (connections[1] == null) { DrawHangingWire(spriteBatch, nodes.Last() + drawOffset, depth); }
235  }
236  if (IsActive && item.ParentInventory?.Owner is Character user && user == Character.Controlled)// && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance)
237  {
238  if (user.CanInteract && currLength < MaxLength)
239  {
240  Vector2 gridPos = Character.Controlled.Position;
241  Vector2 roundedGridPos = new Vector2(
242  MathUtils.RoundTowardsClosest(Character.Controlled.Position.X, Submarine.GridSize.X),
243  MathUtils.RoundTowardsClosest(Character.Controlled.Position.Y, Submarine.GridSize.Y));
244  //Vector2 attachPos = GetAttachPosition(user);
245 
246  if (item.Submarine == null)
247  {
249  if (attachTarget != null)
250  {
251  if (attachTarget.Submarine != null)
252  {
253  //set to submarine-relative position
254  gridPos += attachTarget.Submarine.Position;
255  roundedGridPos += attachTarget.Submarine.Position;
256  }
257  }
258  }
259  else
260  {
261  gridPos += item.Submarine.Position;
262  roundedGridPos += item.Submarine.Position;
263  }
264 
266  {
267  Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.25f);
268  }
269 
271  spriteBatch, wireSprite,
272  nodes[^1] + drawOffset,
273  new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
274  overrideColor ?? item.Color, 0.0f, Width);
275 
277  spriteBatch, wireSprite,
278  new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
280  overrideColor ?? item.Color, itemDepth, Width);
281 
282  GUI.DrawRectangle(spriteBatch, new Vector2(newNodePos.X + drawOffset.X, -(newNodePos.Y + drawOffset.Y)) - Vector2.One * 3, Vector2.One * 6, item.Color);
283  }
284  else
285  {
287  spriteBatch, wireSprite,
288  nodes[^1] + drawOffset,
290  overrideColor ?? item.Color, 0.0f, Width);
291  }
292  }
293  }
294 
295 
297  {
298  DebugDraw(spriteBatch, alpha: 0.2f);
299  }
300 
301  if (!editing || !GameMain.SubEditorScreen.WiringMode) { return; }
302 
303  for (int i = 0; i < nodes.Count; i++)
304  {
305  Vector2 drawPos = nodes[i];
307  drawPos.Y = -drawPos.Y;
308 
309  if ((highlightedNodeIndex == i && item.IsHighlighted) || (selectedNodeIndex == i && item.IsSelected))
310  {
311  GUI.DrawRectangle(spriteBatch, drawPos + new Vector2(-10, -10), new Vector2(20, 20), editorHighlightColor, false, 0.0f);
312  }
313 
314  if (item.IsSelected)
315  {
316  GUI.DrawRectangle(spriteBatch, drawPos + new Vector2(-5, -5), new Vector2(10, 10), item.Color, true, 0.0f);
317 
318  }
319  else
320  {
321  GUI.DrawRectangle(spriteBatch, drawPos + new Vector2(-3, -3), new Vector2(6, 6), item.Color, true, 0.015f);
322  }
323  }
324  }
325 
326  public void DebugDraw(SpriteBatch spriteBatch, float alpha = 1.0f)
327  {
328  if (sections.Count == 0 || Hidden)
329  {
330  return;
331  }
332 
333  const float PowerPulseSpeedLow = 5.0f;
334  const float PowerPulseSpeedHigh = 10.0f;
335  const float PowerHighlightScaleLow = 1.5f;
336  const float PowerHighlightScaleHigh = 2.5f;
337 
338  const float SignalIndicatorInterval = 15.0f;
339  const float SignalIndicatorSpeed = 100.0f;
340 
341  Vector2 drawOffset = GetDrawOffset();
342 
343  Color currentHighlightColor = Color.Transparent;
344  float highlightScale = 0.0f;
345  if (connections[0] != null && connections[1] != null)
346  {
347  float voltage = Math.Max(GetVoltage(0), GetVoltage(1));
348  float GetVoltage(int connectionIndex)
349  {
350  var connection1 = connections[connectionIndex];
351  var connection2 = connections[1 - connectionIndex];
352  if (connection1.IsOutput && connection1.Grid is { Power: > 0.01f } grid1)
353  {
354  if (connection2.Item.GetComponent<Powered>() is Powered powered &&
355  (powered.GetCurrentPowerConsumption(connection2) > 0 || powered is PowerTransfer))
356  {
357  return grid1.Voltage;
358  }
359  }
360  return 0.0f;
361  }
362  if (voltage > 0.0f)
363  {
364  //pulse faster when there's overvoltage
365  float pulseSpeed = voltage > 1.2f ? PowerPulseSpeedHigh : PowerPulseSpeedLow;
366  float pulseAmount = (MathF.Sin((float)Timing.TotalTimeUnpaused * pulseSpeed) + 1.5f) / 2.5f;
367  voltage = Math.Min(voltage, 1.0f);
368  highlightScale = MathHelper.Lerp(PowerHighlightScaleLow, PowerHighlightScaleHigh, voltage);
369  currentHighlightColor = Color.Red * voltage * pulseAmount;
370  }
371  }
372  if (highlightScale > 0.0f)
373  {
374  foreach (WireSection section in sections)
375  {
376  section.Draw(spriteBatch, wireSprite, currentHighlightColor * alpha, drawOffset, 0.0f, Width * highlightScale);
377  }
378  }
379 
380  float signalDuration = (float)Timing.TotalTimeUnpaused - lastReceivedSignal.TimeSent;
381  if (ConnectionPanel.ShouldDebugDrawWiring && signalDuration < 1.0f)
382  {
383  //make some wires "off sync" so it's easier to differentiate signals on overlapping wires
384  float offset = item.ID % 2 == 1 ? SignalIndicatorInterval / 2 : 0.0f;
385  float signalProgress = ((float)(Timing.TotalTimeUnpaused * SignalIndicatorSpeed + offset) % SignalIndicatorInterval) * lastReceivedSignal.Direction;
386  foreach (WireSection section in sections)
387  {
388  for (float x = 0; x < section.Length; x += SignalIndicatorInterval)
389  {
390  Vector2 dir = (section.End - section.Start) / section.Length;
391  float posOnSection = x + signalProgress;
392  if (posOnSection < 0 || posOnSection > section.Length) { continue; }
393  Vector2 signalPos = section.Start + drawOffset + dir * posOnSection;
394  float a = 1.0f - Vector2.Distance(Screen.Selected.Cam.WorldViewCenter, signalPos) / 500.0f;
395  if (a < 0) { continue; }
396  signalPos.Y = -signalPos.Y;
397  GUI.DrawRectangle(spriteBatch, signalPos - Vector2.One * 2.5f, Vector2.One * 5, lastReceivedSignal.Color * a * (1.0f - signalDuration) * alpha, isFilled: true);
398  }
399  }
400  }
401  }
402 
403  private Vector2 GetDrawOffset()
404  {
405  Submarine sub = item.Submarine;
406  if (IsActive && sub == null) // currently being rewired, we need to get the sub from the connections in case the wire has been taken outside
407  {
408  if (connections[0] != null && connections[0].Item.Submarine != null) { sub = connections[0].Item.Submarine; }
409  if (connections[1] != null && connections[1].Item.Submarine != null) { sub = connections[1].Item.Submarine; }
410  }
411 
412  if (sub == null)
413  {
414  return Vector2.Zero;
415  }
416  else
417  {
418  return sub.DrawPosition + sub.HiddenSubPosition;
419  }
420  }
421 
422  private void DrawHangingWire(SpriteBatch spriteBatch, Vector2 start, float depth)
423  {
424  float angle = (float)Math.Sin(GameMain.GameScreen.GameTime * 2.0f + item.ID) * 0.2f;
425  Vector2 endPos = start + new Vector2((float)Math.Sin(angle), -(float)Math.Cos(angle)) * 50.0f;
426 
427  WireSection.Draw(
428  spriteBatch, wireSprite,
429  start, endPos,
430  GUIStyle.Orange, depth + 0.00001f, 0.2f);
431 
432  WireSection.Draw(
433  spriteBatch, wireSprite,
434  start, start + (endPos - start) * 0.7f,
435  item.Color, depth, 0.3f);
436  }
437 
438  public static void UpdateEditing(List<Wire> wires)
439  {
440  var doubleClicked = PlayerInput.DoubleClicked();
441 
442  Wire equippedWire = Character.Controlled.HeldItems.FirstOrDefault(it => it.GetComponent<Wire>() != null)?.GetComponent<Wire>();
443  if (equippedWire != null && GUI.MouseOn == null)
444  {
446  {
447  equippedWire.Use(1.0f, Character.Controlled);
448  }
449  return;
450  }
451 
452  //dragging a node of some wire
453  if (draggingWire != null && !doubleClicked)
454  {
455  if (Character.Controlled != null)
456  {
460  }
461  //cancel dragging
463  {
464  draggingWire = null;
465  selectedNodeIndex = null;
466  }
467  //update dragging
468  else
469  {
470  MapEntity.DisableSelect = true;
471 
472  Submarine sub = draggingWire.item.Submarine;
473  if (draggingWire.connections[0] != null && draggingWire.connections[0].Item.Submarine != null) sub = draggingWire.connections[0].Item.Submarine;
474  if (draggingWire.connections[1] != null && draggingWire.connections[1].Item.Submarine != null) sub = draggingWire.connections[1].Item.Submarine;
475 
477  if (sub != null)
478  {
479  nodeWorldPos = nodeWorldPos - sub.HiddenSubPosition - sub.Position;
480  }
481 
482  if (selectedNodeIndex.HasValue && selectedNodeIndex.Value >= draggingWire.nodes.Count) { selectedNodeIndex = null; }
483  if (highlightedNodeIndex.HasValue && highlightedNodeIndex.Value >= draggingWire.nodes.Count) { highlightedNodeIndex = null; }
484 
485  if (selectedNodeIndex.HasValue)
486  {
487  if (!PlayerInput.IsShiftDown())
488  {
489  nodeWorldPos.X = MathUtils.Round(nodeWorldPos.X, Submarine.GridSize.X / 2.0f);
490  nodeWorldPos.Y = MathUtils.Round(nodeWorldPos.Y, Submarine.GridSize.Y / 2.0f);
491  }
492 
493  draggingWire.nodes[(int)selectedNodeIndex] = nodeWorldPos;
494  draggingWire.UpdateSections();
495  }
496  else
497  {
498  float dragDistance = Submarine.GridSize.X * Submarine.GridSize.Y;
499  dragDistance *= 0.5f;
500  if ((highlightedNodeIndex.HasValue && Vector2.DistanceSquared(nodeWorldPos, draggingWire.nodes[(int)highlightedNodeIndex]) >= dragDistance) ||
502  {
503  selectedNodeIndex = highlightedNodeIndex;
504  }
505  }
506 
507  MapEntity.SelectEntity(draggingWire.item);
508  }
509 
510  return;
511  }
512 
513  bool updateHighlight = true;
514 
515  //a wire has been selected -> check if we should start dragging one of the nodes
516  float nodeSelectDist = 10, sectionSelectDist = 5;
517  highlightedNodeIndex = null;
518  if (MapEntity.SelectedList.Count == 1 && MapEntity.SelectedList.FirstOrDefault() is Item selectedItem)
519  {
520  Wire selectedWire = selectedItem.GetComponent<Wire>();
521 
522  if (selectedWire != null)
523  {
525  if (selectedWire.item.Submarine != null) mousePos -= (selectedWire.item.Submarine.Position + selectedWire.item.Submarine.HiddenSubPosition);
526 
527  //left click while holding ctrl -> check if the cursor is on a wire section,
528  //and add a new node if it is
529  if (PlayerInput.KeyDown(Keys.RightControl) || PlayerInput.KeyDown(Keys.LeftControl))
530  {
532  {
533  if (Character.Controlled != null)
534  {
537  }
538  int closestSectionIndex = selectedWire.GetClosestSectionIndex(mousePos, sectionSelectDist, out _);
539  if (closestSectionIndex > -1)
540  {
541  selectedWire.nodes.Insert(closestSectionIndex + 1, mousePos);
542  selectedWire.UpdateSections();
543  }
544  }
545  }
546  else
547  {
548  //check if close enough to a node
549  int closestIndex = selectedWire.GetClosestNodeIndex(mousePos, nodeSelectDist, out _);
550  if (closestIndex > -1)
551  {
552  highlightedNodeIndex = closestIndex;
553 
554  Vector2 nudge = MapEntity.GetNudgeAmount(doHold: false);
555  if (nudge != Vector2.Zero && closestIndex < selectedWire.nodes.Count)
556  {
557  selectedWire.MoveNode(closestIndex, nudge);
558  }
559 
560  //start dragging the node
562  {
563  if (Character.Controlled != null)
564  {
567  }
568  draggingWire = selectedWire;
569  //selectedNodeIndex = closestIndex;
570  return;
571  }
572  //remove the node
573  else if (PlayerInput.SecondaryMouseButtonClicked() && closestIndex > 0 && closestIndex < selectedWire.nodes.Count - 1)
574  {
575  selectedWire.nodes.RemoveAt(closestIndex);
576  selectedWire.UpdateSections();
577  }
578  // if only one end of the wire is disconnect pick it back up with double click
579  else if (doubleClicked && equippedWire == null && Character.Controlled != null && selectedWire.connections.Any(conn => conn != null))
580  {
581  if (selectedWire.connections[0] == null && closestIndex == 0 || selectedWire.connections[1] == null && closestIndex == selectedWire.nodes.Count - 1)
582  {
583  selectedWire.IsActive = true;
584  selectedWire.nodes.RemoveAt(closestIndex);
585  selectedWire.UpdateSections();
586 
587  // flip the wire
588  if (closestIndex == 0)
589  {
590  selectedWire.nodes.Reverse();
591  selectedWire.connections[0] = selectedWire.connections[1];
592  selectedWire.connections[1] = null;
593  }
594 
595  selectedWire.shouldClearConnections = false;
596  Character.Controlled.Inventory.TryPutItem(selectedWire.item, Character.Controlled, new List<InvSlotType> { InvSlotType.LeftHand, InvSlotType.RightHand });
597  foreach (var entity in MapEntity.MapEntityList)
598  {
599  if (entity is Item item)
600  {
601  item.GetComponent<ConnectionPanel>()?.DisconnectedWires.Remove(selectedWire);
602  }
603  }
604  MapEntity.SelectedList.Clear();
605  selectedWire.shouldClearConnections = true;
606  updateHighlight = false;
607  }
608  }
609  }
610  }
611  }
612  }
613 
614  Wire highlighted = null;
615 
616  //check which wire is highlighted with the cursor
617  if (GUI.MouseOn == null)
618  {
619  float closestDist = float.PositiveInfinity;
620  foreach (Wire w in wires)
621  {
623  if (w.item.Submarine != null) { mousePos -= (w.item.Submarine.Position + w.item.Submarine.HiddenSubPosition); }
624 
625  int highlightedNode = w.GetClosestNodeIndex(mousePos, highlighted == null ? nodeSelectDist : closestDist, out float dist);
626  if (highlightedNode > -1)
627  {
628  if (dist < closestDist)
629  {
630  highlightedNodeIndex = highlightedNode;
631  highlighted = w;
632  closestDist = dist;
633  }
634  }
635 
636  if (w.GetClosestSectionIndex(mousePos, highlighted == null ? sectionSelectDist : closestDist, out dist) > -1)
637  {
638  //prefer nodes over sections
639  if (dist + nodeSelectDist * 0.5f < closestDist)
640  {
641  highlightedNodeIndex = null;
642  highlighted = w;
643  closestDist = dist + nodeSelectDist * 0.5f;
644  }
645  }
646  }
647  }
648 
649  if (highlighted != null && updateHighlight)
650  {
651  highlighted.item.IsHighlighted = true;
652  if (PlayerInput.PrimaryMouseButtonClicked())
653  {
654  MapEntity.DisableSelect = true;
655  MapEntity.SelectEntity(highlighted.item);
656  }
657  }
658  }
659 
660  public override void Move(Vector2 amount, bool ignoreContacts = false)
661  {
662  //only used in the sub editor, hence only in the client project
663  if (!item.IsSelected) { return; }
664 
665  Vector2 wireNodeOffset = item.Submarine == null ? Vector2.Zero : item.Submarine.HiddenSubPosition + amount;
666  for (int i = 0; i < nodes.Count; i++)
667  {
668  if (i == 0 || i == nodes.Count - 1)
669  {
670  if (connections[0]?.Item != null && !connections[0].Item.IsSelected &&
671  (Submarine.RectContains(connections[0].Item.Rect, nodes[i] + wireNodeOffset) || Submarine.RectContains(connections[0].Item.Rect, nodes[i] + wireNodeOffset - amount)))
672  {
673  continue;
674  }
675  else if (connections[1]?.Item != null && !connections[1].Item.IsSelected &&
676  (Submarine.RectContains(connections[1].Item.Rect, nodes[i] + wireNodeOffset) || Submarine.RectContains(connections[1].Item.Rect, nodes[i] + wireNodeOffset - amount)))
677  {
678  continue;
679  }
680  }
681  nodes[i] += amount;
682  }
683  UpdateSections();
684  }
685  public bool IsMouseOn()
686  {
687  if (GUI.MouseOn == null)
688  {
690  if (item.Submarine != null) { mousePos -= (item.Submarine.Position + item.Submarine.HiddenSubPosition); }
691 
692  if (GetClosestNodeIndex(mousePos, 10, out _) > -1) { return true; }
693  if (GetClosestSectionIndex(mousePos, 10, out _) > -1) { return true; }
694  }
695 
696  return false;
697  }
698 
699  public void ClientEventRead(IReadMessage msg, float sendingTime)
700  {
701  int eventIndex = msg.ReadRangedInteger(0, (int)Math.Ceiling(MaxNodeCount / (float)MaxNodesPerNetworkEvent));
702  int nodeCount = msg.ReadRangedInteger(0, MaxNodesPerNetworkEvent);
703  int nodeStartIndex = eventIndex * MaxNodesPerNetworkEvent;
704 
705  Vector2[] nodePositions = new Vector2[nodeStartIndex + nodeCount];
706  for (int i = 0; i < nodes.Count && i < nodePositions.Length; i++)
707  {
708  nodePositions[i] = nodes[i];
709  }
710 
711  for (int i = 0; i < nodeCount; i++)
712  {
713  nodePositions[nodeStartIndex + i] = new Vector2(msg.ReadSingle(), msg.ReadSingle());
714  }
715 
716  if (nodePositions.Any(n => !MathUtils.IsValid(n)))
717  {
718  nodes.Clear();
719  return;
720  }
721 
722  nodes = nodePositions.ToList();
723  UpdateSections();
724  Drawable = nodes.Any();
725  IsActive =
726  (connections[0] == null ^ connections[1] == null) &&
727  (item.ParentInventory is CharacterInventory characterInventory && ((characterInventory.Owner as Character)?.HasEquippedItem(item) ?? false));
728  }
729 
730  public override bool ValidateEventData(NetEntityEvent.IData data)
731  => TryExtractEventData<ClientEventData>(data, out _);
732 
733  public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
734  {
735  var eventData = ExtractEventData<ClientEventData>(extraData);
736  int nodeCount = eventData.NodeCount;
737  msg.WriteByte((byte)nodeCount);
738  if (nodeCount > 0)
739  {
740  msg.WriteSingle(nodes.Last().X);
741  msg.WriteSingle(nodes.Last().Y);
742  }
743  }
744  }
745 }
Vector2 ScreenToWorld(Vector2 coords)
Definition: Camera.cs:410
Vector2 WorldViewCenter
Definition: Camera.cs:126
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
bool DisableInteract
Prevents the character from interacting with items or characters
IEnumerable< Item >?? HeldItems
Items the character has in their hand slots. Doesn't return nulls and only returns items held in both...
override bool TryPutItem(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true, bool ignoreCondition=false)
If there is room, puts the item in the inventory and returns true, otherwise returns false
virtual Vector2 DrawPosition
Definition: Entity.cs:51
Submarine Submarine
Definition: Entity.cs:53
readonly ushort ID
Unique, but non-persistent identifier. Stays the same if the entities are created in the exactly same...
Definition: Entity.cs:43
static SubEditorScreen SubEditorScreen
Definition: GameMain.cs:68
static GameScreen GameScreen
Definition: GameMain.cs:52
void Draw(ISpriteBatch spriteBatch, Sprite wireSprite, Color color, Vector2 offset, float depth, float width=0.3f)
static void Draw(ISpriteBatch spriteBatch, Sprite wireSprite, Vector2 start, Vector2 end, Color color, float depth, float width=0.3f)
override void Move(Vector2 amount, bool ignoreContacts=false)
void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth=-1, Color? overrideColor=null)
override bool ValidateEventData(NetEntityEvent.IData data)
readonly record struct VisualSignal(float TimeSent, Color Color, int Direction)
void Draw(SpriteBatch spriteBatch, bool editing, Vector2 offset, float itemDepth=-1, Color? overrideColor=null)
void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData=null)
override bool Use(float deltaTime, Character character=null)
static Vector2 GetNudgeAmount(bool doHold=true)
static readonly List< MapEntity > MapEntityList
static bool KeyDown(InputType inputType)
static Structure GetAttachTarget(Vector2 worldPosition)
Checks if there's a structure items can be attached to at the given position and returns it.
static bool IsWiringMode()
static void DrawGrid(SpriteBatch spriteBatch, int gridCells, Vector2 gridCenter, Vector2 roundedGridCenter, float alpha=1.0f)
static bool RectContains(Rectangle rect, Vector2 pos, bool inclusive=false)
Interface for entities that the clients can send events to the server
int ReadRangedInteger(int min, int max)
Interface for entities that the server can send events to the clients