2 using Microsoft.Xna.Framework;
3 using Microsoft.Xna.Framework.Graphics;
4 using Microsoft.Xna.Framework.Input;
6 using System.Collections.Generic;
13 private readonly
struct ClientEventData : IEventData
15 public readonly
int NodeCount;
17 public ClientEventData(
int nodeCount)
19 NodeCount = nodeCount;
32 private float cachedWidth = 0f;
34 private void RecalculateVertices(
Sprite wireSprite,
float width)
36 if (MathUtils.NearlyEqual(cachedWidth, width)) {
return; }
39 vertices =
new VertexPositionColorTexture[4];
41 Vector2 expandDir = start-end;
42 expandDir.Normalize();
43 float temp = expandDir.X;
44 expandDir.X = -expandDir.Y;
49 expandDir *= width * srcRect.Height * 0.5f;
51 Vector2 rectLocation = srcRect.Location.ToVector2();
52 Vector2 rectSize = srcRect.Size.ToVector2();
53 Vector2 textureSize =
new Vector2(wireSprite.
Texture.Width, wireSprite.
Texture.Height);
55 Vector2 topLeftUv = rectLocation / textureSize;
56 Vector2 bottomRightUv = (rectLocation + rectSize) / textureSize;
58 Vector2 invStart =
new Vector2(start.X, -start.Y);
59 Vector2 invEnd =
new Vector2(end.X, -end.Y);
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);
69 public void Draw(ISpriteBatch spriteBatch,
Sprite wireSprite, Color color, Vector2 offset,
float depth,
float width = 0.3f)
71 if (width <= 0f) {
return; }
72 RecalculateVertices(wireSprite, width);
74 for (
int i = 0; i <
vertices.Length; i++)
87 public static void Draw(ISpriteBatch spriteBatch,
Sprite wireSprite, Vector2 start, Vector2 end, Color color,
float depth,
float width = 0.3f)
92 spriteBatch.Draw(wireSprite.
Texture,
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),
101 private static Sprite defaultWireSprite;
102 private Sprite overrideSprite;
103 private Sprite wireSprite;
105 private static Wire draggingWire;
106 private static int? selectedNodeIndex;
107 private static int? highlightedNodeIndex;
118 get {
return sectionExtents; }
135 defaultWireSprite ??=
136 new Sprite(
"Content/Items/Electricity/signalcomp.png",
new Rectangle(970, 47, 14, 16),
new Vector2(0.5f, 0.5f))
141 Sprite overrideSprite =
null;
142 foreach (var subElement
in element.Elements())
144 if (subElement.Name.ToString().Equals(
"wiresprite", StringComparison.OrdinalIgnoreCase))
146 overrideSprite =
new Sprite(subElement);
151 return overrideSprite ?? defaultWireSprite;
157 if (wireSprite != defaultWireSprite) { overrideSprite = wireSprite; }
163 (
float)Timing.TotalTimeUnpaused,
164 GetSignalColor(signal),
165 Direction: source == connections[0] ? 1 : -1);
168 private static readonly Color[] dataSignalColors =
new Color[] { Color.White, Color.LightBlue, Color.CornflowerBlue, Color.Blue, Color.BlueViolet, Color.Violet };
170 private static Color GetSignalColor(Signal signal)
172 if (signal.value ==
"0")
176 else if (signal.value ==
"1")
178 return Color.LightGreen;
180 else if (
float.TryParse(signal.value, out
float floatValue))
184 return ToolBox.GradientLerp(Math.Abs(floatValue / 200.0f), dataSignalColors);
186 return Color.LightBlue;
189 public void Draw(SpriteBatch spriteBatch,
bool editing,
float itemDepth = -1, Color? overrideColor =
null)
191 Draw(spriteBatch, editing, Vector2.Zero, itemDepth, overrideColor);
194 public void Draw(SpriteBatch spriteBatch,
bool editing, Vector2 offset,
float itemDepth = -1, Color? overrideColor =
null)
202 Vector2 drawOffset = GetDrawOffset() + offset;
211 section.
Draw(spriteBatch, wireSprite,
213 drawOffset, depth + 0.00001f,
Width * 2.0f);
226 section.
Draw(spriteBatch, wireSprite, overrideColor ??
item.
Color, drawOffset, depth,
Width);
233 if (connections[0] ==
null) { DrawHangingWire(spriteBatch, nodes[0] + drawOffset, depth); }
234 if (connections[1] ==
null) { DrawHangingWire(spriteBatch, nodes.Last() + drawOffset, depth); }
238 if (user.CanInteract && currLength <
MaxLength)
241 Vector2 roundedGridPos =
new Vector2(
249 if (attachTarget !=
null)
271 spriteBatch, wireSprite,
272 nodes[^1] + drawOffset,
273 new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
277 spriteBatch, wireSprite,
278 new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
282 GUI.DrawRectangle(spriteBatch,
new Vector2(newNodePos.X + drawOffset.X, -(newNodePos.Y + drawOffset.Y)) - Vector2.One * 3, Vector2.One * 6,
item.
Color);
287 spriteBatch, wireSprite,
288 nodes[^1] + drawOffset,
303 for (
int i = 0; i < nodes.Count; i++)
305 Vector2 drawPos = nodes[i];
307 drawPos.Y = -drawPos.Y;
311 GUI.DrawRectangle(spriteBatch, drawPos +
new Vector2(-10, -10),
new Vector2(20, 20),
editorHighlightColor,
false, 0.0f);
316 GUI.DrawRectangle(spriteBatch, drawPos +
new Vector2(-5, -5),
new Vector2(10, 10),
item.
Color,
true, 0.0f);
321 GUI.DrawRectangle(spriteBatch, drawPos +
new Vector2(-3, -3),
new Vector2(6, 6),
item.
Color,
true, 0.015f);
326 public void DebugDraw(SpriteBatch spriteBatch,
float alpha = 1.0f)
328 if (sections.Count == 0 ||
Hidden)
333 const float PowerPulseSpeedLow = 5.0f;
334 const float PowerPulseSpeedHigh = 10.0f;
335 const float PowerHighlightScaleLow = 1.5f;
336 const float PowerHighlightScaleHigh = 2.5f;
338 const float SignalIndicatorInterval = 15.0f;
339 const float SignalIndicatorSpeed = 100.0f;
341 Vector2 drawOffset = GetDrawOffset();
343 Color currentHighlightColor = Color.Transparent;
344 float highlightScale = 0.0f;
345 if (connections[0] !=
null && connections[1] !=
null)
347 float voltage = Math.Max(GetVoltage(0), GetVoltage(1));
348 float GetVoltage(
int connectionIndex)
350 var connection1 = connections[connectionIndex];
351 var connection2 = connections[1 - connectionIndex];
352 if (connection1.IsOutput && connection1.Grid is { Power: > 0.01f } grid1)
354 if (connection2.Item.GetComponent<
Powered>() is
Powered powered &&
355 (powered.GetCurrentPowerConsumption(connection2) > 0 || powered is
PowerTransfer))
357 return grid1.Voltage;
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;
372 if (highlightScale > 0.0f)
376 section.
Draw(spriteBatch, wireSprite, currentHighlightColor * alpha, drawOffset, 0.0f,
Width * highlightScale);
380 float signalDuration = (float)Timing.TotalTimeUnpaused - lastReceivedSignal.TimeSent;
384 float offset =
item.
ID % 2 == 1 ? SignalIndicatorInterval / 2 : 0.0f;
385 float signalProgress = ((float)(Timing.TotalTimeUnpaused * SignalIndicatorSpeed + offset) % SignalIndicatorInterval) * lastReceivedSignal.Direction;
388 for (
float x = 0; x < section.
Length; x += SignalIndicatorInterval)
391 float posOnSection = x + signalProgress;
392 if (posOnSection < 0 || posOnSection > section.
Length) {
continue; }
393 Vector2 signalPos = section.
Start + drawOffset + dir * posOnSection;
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);
403 private Vector2 GetDrawOffset()
422 private void DrawHangingWire(SpriteBatch spriteBatch, Vector2 start,
float depth)
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;
428 spriteBatch, wireSprite,
430 GUIStyle.Orange, depth + 0.00001f, 0.2f);
433 spriteBatch, wireSprite,
434 start, start + (endPos - start) * 0.7f,
443 if (equippedWire !=
null && GUI.MouseOn ==
null)
453 if (draggingWire !=
null && !doubleClicked)
465 selectedNodeIndex =
null;
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;
482 if (selectedNodeIndex.HasValue && selectedNodeIndex.Value >= draggingWire.nodes.Count) { selectedNodeIndex =
null; }
483 if (highlightedNodeIndex.HasValue && highlightedNodeIndex.Value >= draggingWire.nodes.Count) { highlightedNodeIndex =
null; }
485 if (selectedNodeIndex.HasValue)
493 draggingWire.nodes[(int)selectedNodeIndex] = nodeWorldPos;
494 draggingWire.UpdateSections();
499 dragDistance *= 0.5f;
500 if ((highlightedNodeIndex.HasValue && Vector2.DistanceSquared(nodeWorldPos, draggingWire.nodes[(
int)highlightedNodeIndex]) >= dragDistance) ||
503 selectedNodeIndex = highlightedNodeIndex;
513 bool updateHighlight =
true;
516 float nodeSelectDist = 10, sectionSelectDist = 5;
517 highlightedNodeIndex =
null;
520 Wire selectedWire = selectedItem.GetComponent<
Wire>();
522 if (selectedWire !=
null)
525 if (selectedWire.item.Submarine !=
null) mousePos -= (selectedWire.item.Submarine.Position + selectedWire.item.Submarine.HiddenSubPosition);
538 int closestSectionIndex = selectedWire.GetClosestSectionIndex(mousePos, sectionSelectDist, out _);
539 if (closestSectionIndex > -1)
541 selectedWire.nodes.Insert(closestSectionIndex + 1, mousePos);
542 selectedWire.UpdateSections();
549 int closestIndex = selectedWire.GetClosestNodeIndex(mousePos, nodeSelectDist, out _);
550 if (closestIndex > -1)
552 highlightedNodeIndex = closestIndex;
555 if (nudge != Vector2.Zero && closestIndex < selectedWire.nodes.Count)
557 selectedWire.MoveNode(closestIndex, nudge);
568 draggingWire = selectedWire;
575 selectedWire.nodes.RemoveAt(closestIndex);
576 selectedWire.UpdateSections();
579 else if (doubleClicked && equippedWire ==
null &&
Character.
Controlled !=
null && selectedWire.connections.Any(conn => conn !=
null))
581 if (selectedWire.connections[0] ==
null && closestIndex == 0 || selectedWire.connections[1] ==
null && closestIndex == selectedWire.nodes.Count - 1)
583 selectedWire.IsActive =
true;
584 selectedWire.nodes.RemoveAt(closestIndex);
585 selectedWire.UpdateSections();
588 if (closestIndex == 0)
590 selectedWire.nodes.Reverse();
591 selectedWire.connections[0] = selectedWire.connections[1];
592 selectedWire.connections[1] =
null;
595 selectedWire.shouldClearConnections =
false;
605 selectedWire.shouldClearConnections =
true;
606 updateHighlight =
false;
614 Wire highlighted =
null;
617 if (GUI.MouseOn ==
null)
619 float closestDist =
float.PositiveInfinity;
620 foreach (
Wire w
in wires)
623 if (w.item.Submarine !=
null) { mousePos -= (w.item.Submarine.Position + w.item.Submarine.HiddenSubPosition); }
625 int highlightedNode = w.GetClosestNodeIndex(mousePos, highlighted ==
null ? nodeSelectDist : closestDist, out
float dist);
626 if (highlightedNode > -1)
628 if (dist < closestDist)
630 highlightedNodeIndex = highlightedNode;
636 if (w.GetClosestSectionIndex(mousePos, highlighted ==
null ? sectionSelectDist : closestDist, out dist) > -1)
639 if (dist + nodeSelectDist * 0.5f < closestDist)
641 highlightedNodeIndex =
null;
643 closestDist = dist + nodeSelectDist * 0.5f;
649 if (highlighted !=
null && updateHighlight)
651 highlighted.item.IsHighlighted =
true;
652 if (PlayerInput.PrimaryMouseButtonClicked())
654 MapEntity.DisableSelect =
true;
655 MapEntity.SelectEntity(highlighted.item);
660 public override void Move(Vector2 amount,
bool ignoreContacts =
false)
663 if (!item.IsSelected) {
return; }
665 Vector2 wireNodeOffset = item.Submarine ==
null ? Vector2.Zero : item.Submarine.HiddenSubPosition + amount;
666 for (
int i = 0; i < nodes.Count; i++)
668 if (i == 0 || i == nodes.Count - 1)
687 if (GUI.MouseOn ==
null)
690 if (item.Submarine !=
null) { mousePos -= (item.Submarine.Position + item.Submarine.HiddenSubPosition); }
692 if (GetClosestNodeIndex(mousePos, 10, out _) > -1) {
return true; }
693 if (GetClosestSectionIndex(mousePos, 10, out _) > -1) {
return true; }
701 int eventIndex = msg.
ReadRangedInteger(0, (
int)Math.Ceiling(MaxNodeCount / (
float)MaxNodesPerNetworkEvent));
703 int nodeStartIndex = eventIndex * MaxNodesPerNetworkEvent;
705 Vector2[] nodePositions =
new Vector2[nodeStartIndex + nodeCount];
706 for (
int i = 0; i < nodes.Count && i < nodePositions.Length; i++)
708 nodePositions[i] = nodes[i];
711 for (
int i = 0; i < nodeCount; i++)
716 if (nodePositions.Any(n => !MathUtils.IsValid(n)))
722 nodes = nodePositions.ToList();
724 Drawable = nodes.Any();
726 (connections[0] ==
null ^ connections[1] ==
null) &&
727 (item.ParentInventory is
CharacterInventory characterInventory && ((characterInventory.Owner as
Character)?.HasEquippedItem(item) ??
false));
731 => TryExtractEventData<ClientEventData>(data, out _);
735 var eventData = ExtractEventData<ClientEventData>(extraData);
736 int nodeCount = eventData.NodeCount;
Vector2 ScreenToWorld(Vector2 coords)
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
CharacterInventory Inventory
static Character? Controlled
override Vector2 Position
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
readonly ushort ID
Unique, but non-persistent identifier. Stays the same if the entities are created in the exactly same...
static SubEditorScreen SubEditorScreen
static GameScreen GameScreen
Inventory ParentInventory
static bool ShouldDebugDrawWiring
VertexPositionColorTexture[] shiftedVertices
VertexPositionColorTexture[] vertices
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)
static void UpdateEditing(List< Wire > wires)
static Color higlightColor
static Color editorSelectedColor
override bool ValidateEventData(NetEntityEvent.IData data)
void ClientEventRead(IReadMessage msg, float sendingTime)
static Sprite ExtractWireSprite(ContentXElement element)
static Color editorHighlightColor
readonly record struct VisualSignal(float TimeSent, Color Color, int Direction)
void DebugDraw(SpriteBatch spriteBatch, float alpha=1.0f)
void Draw(SpriteBatch spriteBatch, bool editing, Vector2 offset, float itemDepth=-1, Color? overrideColor=null)
void RegisterSignal(Signal signal, Connection source)
void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData=null)
override bool Use(float deltaTime, Character character=null)
static HashSet< MapEntity > SelectedList
static Vector2 GetNudgeAmount(bool doHold=true)
static readonly List< MapEntity > MapEntityList
static void SelectEntity(MapEntity entity)
static bool DisableSelect
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 bool IsSubEditor()
static bool ShouldDrawGrid
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)
Vector2 HiddenSubPosition
static readonly Vector2 GridSize
override Vector2? Position
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
void WriteSingle(Single val)