Unstable 1.1.14.0
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class CircuitBoxComponent
|
||||
{
|
||||
public static Option<GUIComponent> EditingHUD = Option.None;
|
||||
|
||||
private Sprite Sprite => Item.Prefab.InventoryIcon ?? Item.Prefab.Sprite;
|
||||
|
||||
private CircuitBoxLabel? label;
|
||||
private CircuitBoxLabel Label
|
||||
{
|
||||
get
|
||||
{
|
||||
if (label is { } l)
|
||||
{
|
||||
return l;
|
||||
}
|
||||
|
||||
var name = TextManager.Get($"circuitboxnode.{Item.Prefab.Identifier}").Fallback($"[FALLBACK] {Item.Name}");
|
||||
label = new CircuitBoxLabel(name, GUIStyle.LargeFont);
|
||||
return label.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateEditing(RectTransform parent)
|
||||
{
|
||||
if (EditingHUD.TryUnwrap(out var editor))
|
||||
{
|
||||
if (editor.UserData == this) { return; }
|
||||
RemoveEditingHUD();
|
||||
}
|
||||
EditingHUD = Option.Some(CreateEditingHUD(parent));
|
||||
}
|
||||
|
||||
public static void RemoveEditingHUD()
|
||||
{
|
||||
if (!EditingHUD.TryUnwrap(out var editor)) { return; }
|
||||
|
||||
editor.RectTransform.Parent = null;
|
||||
EditingHUD = Option.None;
|
||||
}
|
||||
|
||||
public GUIComponent CreateEditingHUD(RectTransform parent)
|
||||
{
|
||||
GUIFrame frame = new(new RectTransform(new Vector2(0.4f, 0.3f), parent, Anchor.TopRight))
|
||||
{
|
||||
UserData = this
|
||||
};
|
||||
|
||||
GUIListBox listBox = new(new RectTransform(ToolBox.PaddingSizeParentRelative(frame.RectTransform, 0.8f), frame.RectTransform, Anchor.Center))
|
||||
{
|
||||
KeepSpaceForScrollBar = true,
|
||||
AutoHideScrollBar = false,
|
||||
CanTakeKeyBoardFocus = false
|
||||
};
|
||||
|
||||
bool isEditor = Screen.Selected is { IsEditor: true };
|
||||
|
||||
GUILayoutGroup titleHolder = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), listBox.Content.RectTransform));
|
||||
new GUITextBlock(new RectTransform(Vector2.One, titleHolder.RectTransform), Item.Name, font: GUIStyle.LargeFont)
|
||||
{
|
||||
TextColor = Color.White,
|
||||
Color = Color.Black
|
||||
};
|
||||
int fieldCount = 0;
|
||||
|
||||
foreach (ItemComponent ic in Item.Components)
|
||||
{
|
||||
if (ic is Holdable) { continue; }
|
||||
if (!ic.AllowInGameEditing) { continue; }
|
||||
if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0 &&
|
||||
!SerializableProperty.GetProperties<ConditionallyEditable>(ic).Any(p => p.GetAttribute<ConditionallyEditable>().IsEditable(ic)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), listBox.Content.RectTransform), style: "HorizontalLine");
|
||||
|
||||
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame: !isEditor, showName: false, titleFont: GUIStyle.SubHeadingFont);
|
||||
fieldCount += componentEditor.Fields.Count;
|
||||
|
||||
ic.CreateEditingHUD(componentEditor);
|
||||
componentEditor.Recalculate();
|
||||
}
|
||||
|
||||
if (fieldCount == 0)
|
||||
{
|
||||
frame.Visible = false;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
public override void DrawHeader(SpriteBatch spriteBatch, RectangleF drawRect, Color color)
|
||||
{
|
||||
// scale to topRect height
|
||||
Vector2 scale = new(drawRect.Height / MathF.Min(Sprite.size.X, Sprite.size.Y)),
|
||||
spritePosition = new(drawRect.Left, drawRect.Top);
|
||||
|
||||
float spriteWidth = Sprite.size.X * scale.X;
|
||||
|
||||
Sprite.Draw(spriteBatch, spritePosition, Color.White, Vector2.Zero, 0f, scale);
|
||||
GUI.DrawString(spriteBatch, new Vector2(spritePosition.X + spriteWidth + CircuitBoxSizes.NodeHeaderTextPadding, drawRect.Center.Y - Label.Size.Y / 2f), Label.Value, GUIStyle.TextColorNormal, font: GUIStyle.LargeFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#nullable enable
|
||||
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal abstract partial class CircuitBoxConnection
|
||||
{
|
||||
public string Name => Label.Value.Value;
|
||||
public CircuitBoxLabel Label { get; private set; }
|
||||
|
||||
private Sprite? knobSprite,
|
||||
screwSprite,
|
||||
connectorSprite;
|
||||
|
||||
private static int padding => GUI.IntScale(8);
|
||||
|
||||
private Option<LocalizedString> tooltip = Option.None;
|
||||
|
||||
private partial void InitProjSpecific(CircuitBox circuitBox)
|
||||
{
|
||||
Label = new CircuitBoxLabel(Connection.Name, GUIStyle.SubHeadingFont);
|
||||
knobSprite = circuitBox.ConnectionSprite;
|
||||
screwSprite = circuitBox.ConnectionScrewSprite;
|
||||
connectorSprite = circuitBox.WireConnectorSprite;
|
||||
Length = Rect.Width + padding + Label.Size.X;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Vector2 drawPos, Vector2 parentPos, Color color)
|
||||
{
|
||||
if (CircuitBox.UI is not { } circuitBoxUi) { return; }
|
||||
var drawRect = CircuitBoxNode.OverrideRectLocation(Rect, drawPos, parentPos);
|
||||
|
||||
Vector2 cursorPos = circuitBoxUi.GetCursorPosition();
|
||||
cursorPos.Y = -cursorPos.Y;
|
||||
|
||||
bool isMouseOver = drawRect.Contains(cursorPos);
|
||||
|
||||
float xPos;
|
||||
if (IsOutput)
|
||||
{
|
||||
xPos = drawRect.Left - padding - Label.Size.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
xPos = drawRect.Right + padding;
|
||||
}
|
||||
|
||||
Vector2 stringPos = new Vector2(xPos, drawRect.Center.Y - Label.Size.Y / 2f);
|
||||
GUI.DrawString(spriteBatch, stringPos, Label.Value, GUIStyle.TextColorNormal, font: Label.Font);
|
||||
|
||||
if (knobSprite is null)
|
||||
{
|
||||
CircuitBoxUI.DrawRectangleWithBorder(spriteBatch, drawRect, GUIStyle.Blue * 0.3f, GUIStyle.Blue);
|
||||
}
|
||||
else
|
||||
{
|
||||
float scale = drawRect.Height / knobSprite.size.Y;
|
||||
knobSprite?.Draw(spriteBatch, drawRect.Center, color, 0f, scale);
|
||||
}
|
||||
|
||||
bool isScrewed = this switch
|
||||
{
|
||||
CircuitBoxOutputConnection output => output.ExternallyConnectedFrom.Count > 0,
|
||||
CircuitBoxInputConnection input => input.ExternallyConnectedTo.Count > 0,
|
||||
_ => Connection.Wires.Count > 0 ||
|
||||
Connection.CircuitBoxConnections.Count > 0 ||
|
||||
ExternallyConnectedFrom.Count > 0
|
||||
};
|
||||
|
||||
if (isMouseOver)
|
||||
{
|
||||
var glowSprite = GUIStyle.UIGlowCircular.Value.Sprite;
|
||||
float glowScale = 40f / glowSprite.size.X;
|
||||
if (isScrewed)
|
||||
{
|
||||
glowScale *= 1.2f;
|
||||
}
|
||||
glowSprite.Draw(spriteBatch, position, GUIStyle.Yellow, glowSprite.size / 2, scale: glowScale);
|
||||
}
|
||||
|
||||
tooltip = Option.None;
|
||||
if (ConnectionPanel.ShouldDebugDrawWiring)
|
||||
{
|
||||
Connection.DrawConnectionDebugInfo(spriteBatch, Connection, drawRect.Center, isScrewed ? 1.1f : 0.9f, out var tooltipText);
|
||||
|
||||
if (isMouseOver && !tooltipText.IsNullOrEmpty())
|
||||
{
|
||||
tooltip = Option.Some(tooltipText);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isScrewed) { return; }
|
||||
|
||||
if (screwSprite is not null)
|
||||
{
|
||||
float screwScale = drawRect.Height / screwSprite.size.Y;
|
||||
screwSprite.Draw(spriteBatch, drawRect.Center, color, 0f, screwScale);
|
||||
}
|
||||
|
||||
if (connectorSprite is not null)
|
||||
{
|
||||
float screwScale = drawRect.Height / connectorSprite.size.Y * 2f;
|
||||
Vector2 pos = drawRect.Center;
|
||||
|
||||
connectorSprite.Draw(spriteBatch,
|
||||
pos: pos,
|
||||
color: Color.White,
|
||||
origin: connectorSprite.Origin,
|
||||
rotate: MathHelper.Pi / (IsOutput ? -2f : 2f),
|
||||
scale: screwScale,
|
||||
spriteEffect: SpriteEffects.None);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawHUD(SpriteBatch spriteBatch, Camera camera)
|
||||
{
|
||||
if (!tooltip.TryUnwrap(out var text)) { return; }
|
||||
|
||||
var drawPos = camera.WorldToScreen(new Vector2(Rect.Right, -Rect.Bottom));
|
||||
|
||||
GUIComponent.DrawToolTip(spriteBatch, text, drawPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal readonly struct CircuitBoxLabel
|
||||
{
|
||||
public LocalizedString Value { get; }
|
||||
|
||||
public Vector2 Size { get; }
|
||||
|
||||
public GUIFont Font { get; }
|
||||
|
||||
public CircuitBoxLabel(LocalizedString value, GUIFont font)
|
||||
{
|
||||
Value = value;
|
||||
Font = font;
|
||||
Size = font.MeasureString(font.ForceUpperCase ? value.Value.ToUpperInvariant() : value.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
/// <summary>
|
||||
/// This class handles a couple things:
|
||||
/// - Figuring out which components should be moved when dragging a certain part of the UI.
|
||||
/// - Finding components, connectors and wires under cursor.
|
||||
/// - Determines whether the user is dragging something.
|
||||
/// </summary>
|
||||
internal sealed class CircuitBoxMouseDragSnapshotHandler
|
||||
{
|
||||
public IEnumerable<CircuitBoxNode> Nodes => circuitBoxUi.CircuitBox.Components.Union<CircuitBoxNode>(circuitBoxUi.CircuitBox.InputOutputNodes);
|
||||
|
||||
private IReadOnlyList<CircuitBoxWire> Wires => circuitBoxUi.CircuitBox.Wires;
|
||||
|
||||
// List of all connections in the circuit box
|
||||
private ImmutableArray<CircuitBoxConnection> connections = ImmutableArray<CircuitBoxConnection>.Empty;
|
||||
|
||||
// Nodes that were under cursor when dragging started
|
||||
private ImmutableHashSet<CircuitBoxNode> lastNodesUnderCursor = ImmutableHashSet<CircuitBoxNode>.Empty,
|
||||
// Nodes that were selected when dragging started
|
||||
lastSelectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty,
|
||||
// Nodes that should be moved when dragging
|
||||
moveAffectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty;
|
||||
|
||||
public ImmutableHashSet<CircuitBoxNode> GetLastComponentsUnderCursor() => lastNodesUnderCursor;
|
||||
public ImmutableHashSet<CircuitBoxNode> GetMoveAffectedComponents() => moveAffectedComponents;
|
||||
|
||||
public Option<CircuitBoxConnection> LastConnectorUnderCursor = Option.None;
|
||||
public Option<CircuitBoxWire> LastWireUnderCursor = Option.None;
|
||||
|
||||
/// <summary>
|
||||
/// If the user is currently dragging a node
|
||||
/// </summary>
|
||||
public bool IsDragging { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the user is currently dragging a wire
|
||||
/// </summary>
|
||||
public bool IsWiring { get; private set; }
|
||||
|
||||
private Vector2 startClick = Vector2.Zero;
|
||||
private readonly CircuitBoxUI circuitBoxUi;
|
||||
|
||||
/// <summary>
|
||||
/// How far the user has to drag the mouse while holding down the button before dragging starts
|
||||
/// </summary>
|
||||
private const float dragTreshold = 16f;
|
||||
|
||||
public CircuitBoxMouseDragSnapshotHandler(CircuitBoxUI ui)
|
||||
{
|
||||
circuitBoxUi = ui;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the user holds down the mouse button
|
||||
/// </summary>
|
||||
public void StartDragging()
|
||||
{
|
||||
Vector2 cursorPos = circuitBoxUi.GetCursorPosition();
|
||||
SnapshotNodesUnderCursor(cursorPos);
|
||||
SnapshotSelectedNodes();
|
||||
SnapshotMoveAffectedNodes();
|
||||
startClick = cursorPos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all connections and gathers them into a single list for easier iteration.
|
||||
/// </summary>
|
||||
public void UpdateConnections()
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<CircuitBoxConnection>();
|
||||
|
||||
builder.AddRange(circuitBoxUi.CircuitBox.Inputs);
|
||||
builder.AddRange(circuitBoxUi.CircuitBox.Outputs);
|
||||
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
builder.AddRange(node.Connectors);
|
||||
}
|
||||
|
||||
connections = builder.ToImmutable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a possible connector under the cursor.
|
||||
/// </summary>
|
||||
public Option<CircuitBoxConnection> FindConnectorUnderCursor(Vector2 cursorPos)
|
||||
{
|
||||
foreach (var connection in connections)
|
||||
{
|
||||
if (connection.Contains(cursorPos))
|
||||
{
|
||||
return Option.Some(connection);
|
||||
}
|
||||
}
|
||||
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a possible wire under the cursor.
|
||||
/// </summary>
|
||||
public Option<CircuitBoxWire> FindWireUnderCursor(Vector2 cursorPos)
|
||||
{
|
||||
foreach (CircuitBoxWire wire in Wires)
|
||||
{
|
||||
if (wire is { IsSelected: true, IsSelectedByMe: false }) { continue; }
|
||||
if (wire.Renderer.Contains(cursorPos))
|
||||
{
|
||||
return Option.Some(wire);
|
||||
}
|
||||
}
|
||||
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all nodes that are currently under the cursor that are not selected by someone else.
|
||||
/// </summary>
|
||||
public ImmutableHashSet<CircuitBoxNode> FindNodesUnderCursor(Vector2 cursorPos)
|
||||
{
|
||||
var builder = ImmutableHashSet.CreateBuilder<CircuitBoxNode>();
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
if (node is { IsSelected: true, IsSelectedByMe: false }) { continue; }
|
||||
if (node.Rect.Contains(cursorPos))
|
||||
{
|
||||
builder.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and stores all nodes, connectors and wires that are under the cursor when dragging starts.
|
||||
/// </summary>
|
||||
private void SnapshotNodesUnderCursor(Vector2 cursorPos)
|
||||
{
|
||||
lastNodesUnderCursor = FindNodesUnderCursor(cursorPos);
|
||||
LastConnectorUnderCursor = FindConnectorUnderCursor(cursorPos);
|
||||
LastWireUnderCursor = FindWireUnderCursor(cursorPos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores all nodes that are currently selected when dragging starts.
|
||||
/// There's no real way to change your selection while dragging so this is kinda pointless
|
||||
/// but we snapshot it anyway just in case.
|
||||
/// </summary>
|
||||
private void SnapshotSelectedNodes()
|
||||
{
|
||||
lastSelectedComponents = Nodes.Where(static n => n is { IsSelected: true, IsSelectedByMe: true }).ToImmutableHashSet();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores all nodes that should be moved when dragging starts.
|
||||
/// </summary>
|
||||
private void SnapshotMoveAffectedNodes()
|
||||
{
|
||||
bool moveSelection = lastNodesUnderCursor.Any(node => lastSelectedComponents.Contains(node));
|
||||
|
||||
/*
|
||||
* If the user is dragging a selection, we should move all selected nodes (true).
|
||||
*
|
||||
* But for convenience, if the user is dragging a single node that is not part of the selection,
|
||||
* we should move that node only instead and leave the selection alone. (false)
|
||||
*/
|
||||
moveAffectedComponents = moveSelection switch
|
||||
{
|
||||
true => lastSelectedComponents,
|
||||
false => circuitBoxUi.GetTopmostNode(lastNodesUnderCursor) switch
|
||||
{
|
||||
null => ImmutableHashSet<CircuitBoxNode>.Empty,
|
||||
var node => ImmutableHashSet.Create(node)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Vector2 GetDragAmount(Vector2 mousePos) => mousePos - startClick;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the user releases the mouse button
|
||||
/// </summary>
|
||||
public void EndDragging()
|
||||
{
|
||||
startClick = Vector2.Zero;
|
||||
IsDragging = false;
|
||||
IsWiring = false;
|
||||
lastNodesUnderCursor = ImmutableHashSet<CircuitBoxNode>.Empty;
|
||||
}
|
||||
|
||||
public void UpdateDrag(Vector2 cursorPos)
|
||||
{
|
||||
// if there are no connectors under cursor, we can't be wiring anything
|
||||
if (LastConnectorUnderCursor.IsNone())
|
||||
{
|
||||
IsWiring = false;
|
||||
}
|
||||
|
||||
// if there are no nodes under cursor, we can't be dragging anything
|
||||
if (lastNodesUnderCursor.IsEmpty)
|
||||
{
|
||||
IsDragging = false;
|
||||
}
|
||||
|
||||
// startClick is set to zero when the user releases the mouse button, so we should be neither dragging nor wiring in this state
|
||||
if (startClick == Vector2.Zero)
|
||||
{
|
||||
IsDragging = false;
|
||||
IsWiring = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool isDragTresholdExceeded = Vector2.DistanceSquared(startClick, cursorPos) > dragTreshold * dragTreshold;
|
||||
|
||||
if (LastConnectorUnderCursor.IsNone())
|
||||
{
|
||||
IsDragging |= isDragTresholdExceeded;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsWiring |= isDragTresholdExceeded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class CircuitBoxNode
|
||||
{
|
||||
private RectangleF DrawRect,
|
||||
TopDrawRect;
|
||||
|
||||
private void UpdateDrawRects()
|
||||
{
|
||||
var drawRect = new RectangleF(Position - Size / 2f, Size);
|
||||
drawRect.Y = -drawRect.Y;
|
||||
drawRect.Y -= drawRect.Height;
|
||||
DrawRect = drawRect;
|
||||
|
||||
TopDrawRect = new RectangleF(drawRect.X, drawRect.Y - (CircuitBoxSizes.NodeHeaderHeight - 1), drawRect.Width, CircuitBoxSizes.NodeHeaderHeight);
|
||||
}
|
||||
|
||||
public void OnUICreated()
|
||||
{
|
||||
Size = CalculateSize(Connectors);
|
||||
UpdatePositions();
|
||||
}
|
||||
|
||||
public void DrawBackground(SpriteBatch spriteBatch, RectangleF drawRect, RectangleF topDrawRect, Color color)
|
||||
{
|
||||
CircuitBox.NodeFrameSprite?.Draw(spriteBatch, drawRect, color);
|
||||
CircuitBox.NodeTopSprite?.Draw(spriteBatch, topDrawRect, color);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Vector2 drawPos, Color color)
|
||||
{
|
||||
RectangleF drawRect = OverrideRectLocation(DrawRect, drawPos, Position),
|
||||
topDrawRect = OverrideRectLocation(TopDrawRect, drawPos, Position);
|
||||
|
||||
DrawBackground(spriteBatch, drawRect, topDrawRect, color);
|
||||
DrawHeader(spriteBatch, topDrawRect, color);
|
||||
|
||||
DrawConnectors(spriteBatch, drawPos);
|
||||
}
|
||||
|
||||
public void DrawHUD(SpriteBatch spriteBatch, Camera camera)
|
||||
{
|
||||
foreach (var c in Connectors)
|
||||
{
|
||||
c.DrawHUD(spriteBatch, camera);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void DrawHeader(SpriteBatch spriteBatch, RectangleF rect, Color color) { }
|
||||
|
||||
public void DrawConnectors(SpriteBatch spriteBatch, Vector2 drawPos)
|
||||
{
|
||||
var color = Color.White * Opacity;
|
||||
foreach (var c in Connectors)
|
||||
{
|
||||
c.Draw(spriteBatch, drawPos, Position, color);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSelection(SpriteBatch spriteBatch, Color color)
|
||||
{
|
||||
int pad = GUI.IntScale(8);
|
||||
|
||||
var rect = Rect;
|
||||
rect.Y = -rect.Y;
|
||||
rect.Y -= rect.Height;
|
||||
|
||||
rect.Inflate(pad, pad);
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, rect, color * Opacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the location of the rectangle to a specific position, keeping origin intact.
|
||||
/// </summary>
|
||||
public static RectangleF OverrideRectLocation(RectangleF rect, Vector2 overridePos, Vector2 originalPos)
|
||||
{
|
||||
rect.Location -= new Vector2(originalPos.X, -originalPos.Y);
|
||||
rect.Location += new Vector2(overridePos.X, -overridePos.Y);
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,722 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal sealed class CircuitBoxUI
|
||||
{
|
||||
private readonly Camera camera;
|
||||
private static readonly Vector2 gridSize = new Vector2(128f);
|
||||
public readonly CircuitBox CircuitBox;
|
||||
private bool componentMenuOpen;
|
||||
private float componentMenuOpenState;
|
||||
|
||||
private GUICustomComponent? circuitComponent;
|
||||
private GUIFrame? componentMenu;
|
||||
private GUIButton? toggleMenuButton;
|
||||
private GUIFrame? selectedWireFrame;
|
||||
private GUIListBox? componentList;
|
||||
private GUITextBlock? inventoryIndicatorText;
|
||||
private readonly Sprite cursorSprite = GUIStyle.CursorSprite[CursorState.Default];
|
||||
|
||||
private Option<RectangleF> selection = Option.None;
|
||||
private string searchTerm = string.Empty;
|
||||
|
||||
public static Option<CircuitBoxWireRenderer> DraggedWire = Option.None;
|
||||
|
||||
public readonly CircuitBoxMouseDragSnapshotHandler MouseSnapshotHandler;
|
||||
|
||||
public List<CircuitBoxWireRenderer> VirtualWires = new();
|
||||
|
||||
public CircuitBoxUI(CircuitBox box)
|
||||
{
|
||||
camera = new Camera
|
||||
{
|
||||
MinZoom = 0.25f,
|
||||
MaxZoom = 2f
|
||||
};
|
||||
|
||||
CircuitBox = box;
|
||||
MouseSnapshotHandler = new CircuitBoxMouseDragSnapshotHandler(this);
|
||||
}
|
||||
|
||||
#region UI
|
||||
|
||||
public void CreateGUI(GUIFrame parent)
|
||||
{
|
||||
GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.97f, 0.95f), parent.RectTransform, Anchor.Center), style: null);
|
||||
circuitComponent = new GUICustomComponent(new RectTransform(Vector2.One, paddedFrame.RectTransform), onDraw: (spriteBatch, component) =>
|
||||
{
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
spriteBatch.End();
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = component.Rect;
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable, transformMatrix: camera.Transform);
|
||||
DrawCircuits(spriteBatch);
|
||||
spriteBatch.End();
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
DrawHUD(spriteBatch);
|
||||
spriteBatch.End();
|
||||
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
});
|
||||
|
||||
GUIScissorComponent menuContainer = new GUIScissorComponent(new RectTransform(Vector2.One, paddedFrame.RectTransform, anchor: Anchor.Center))
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
componentMenuOpen = true;
|
||||
componentMenu = new GUIFrame(new RectTransform(new Vector2(1f, 0.4f), menuContainer.Content.RectTransform, Anchor.BottomRight));
|
||||
toggleMenuButton = new GUIButton(new RectTransform(new Point(300, 30), GUI.Canvas) { MinSize = new Point(0, 15) }, style: "UIToggleButtonVertical")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
componentMenuOpen = !componentMenuOpen;
|
||||
foreach (GUIComponent child in btn.Children)
|
||||
{
|
||||
child.SpriteEffects = componentMenuOpen ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GUILayoutGroup menuLayout = new GUILayoutGroup(new RectTransform(Vector2.One, componentMenu.RectTransform), childAnchor: Anchor.TopCenter) { RelativeSpacing = 0.02f };
|
||||
GUILayoutGroup headerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), menuLayout.RectTransform), isHorizontal: true);
|
||||
|
||||
GUILayoutGroup labelLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.33f, 1f), headerLayout.RectTransform), isHorizontal: true);
|
||||
|
||||
GUILayoutGroup searchBarLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.33f, 1f), headerLayout.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true);
|
||||
GUITextBlock searchBarLabel = new GUITextBlock(new RectTransform(new Vector2(0.15f, 1f), searchBarLayout.RectTransform), "Filter");
|
||||
GUITextBox searchbar = new GUITextBox(new RectTransform(new Vector2(0.85f, 1f), searchBarLayout.RectTransform), string.Empty, createClearButton: true);
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(0.5f, 0.01f), menuLayout.RectTransform), style: "HorizontalLine");
|
||||
|
||||
componentList = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.65f), menuLayout.RectTransform))
|
||||
{
|
||||
PlaySoundOnSelect = true,
|
||||
UseGridLayout = true,
|
||||
OnSelected = (_, o) =>
|
||||
{
|
||||
if (o is not ItemPrefab prefab) { return false; }
|
||||
|
||||
CircuitBox.HeldComponent = Option.Some(prefab);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GUILayoutGroup inventoryLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.33f, 1f), headerLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.Center);
|
||||
GUILayoutGroup indicatorLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 1f), inventoryLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
GUIImage indicatorIcon = new GUIImage(new RectTransform(new Vector2(0.5f, 0.8f), indicatorLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "CircuitIndicatorIcon");
|
||||
inventoryIndicatorText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), indicatorLayout.RectTransform), GetInventoryText(), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
int gapSize = GUI.IntScale(8);
|
||||
selectedWireFrame = SubEditorScreen.CreateWiringPanel(Point.Zero, SelectWire);
|
||||
selectedWireFrame.RectTransform.AbsoluteOffset = new Point(parent.Rect.X - (selectedWireFrame.Rect.Width + gapSize), parent.Rect.Y);
|
||||
|
||||
foreach (ItemPrefab prefab in ItemPrefab.Prefabs.OrderBy(static p => p.Name))
|
||||
{
|
||||
if (!prefab.Tags.Contains("circuitboxcomponent")) { continue; }
|
||||
|
||||
CreateComponentElement(prefab, componentList.Content.RectTransform);
|
||||
}
|
||||
|
||||
searchbar.OnTextChanged += (tb, s) =>
|
||||
{
|
||||
searchTerm = s;
|
||||
UpdateComponentList();
|
||||
return true;
|
||||
};
|
||||
int buttonHeight = (int)(GUIStyle.ItemFrameMargin.Y * 0.4f);
|
||||
var settingsIcon = new GUIButton(new RectTransform(new Point(buttonHeight), parent.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(buttonHeight / 4), MinSize = new Point(buttonHeight) },
|
||||
style: "GUIButtonSettings")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu(
|
||||
new ContextMenuOption("circuitboxsetting.resetview", isEnabled: true, onSelected: ResetCamera)
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.resetview")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.find", isEnabled: true,
|
||||
new ContextMenuOption("circuitboxsetting.focusinput", isEnabled: true, onSelected: () => FindInputOuput(CircuitBoxInputOutputNode.Type.Input))
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focusinput")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.focusoutput", isEnabled: true, onSelected: () => FindInputOuput(CircuitBoxInputOutputNode.Type.Output))
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focusoutput")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.focuscircuits", isEnabled: CircuitBox.Components.Any(), onSelected: FindCircuit)
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focuscircuits")
|
||||
}));
|
||||
|
||||
|
||||
void ResetCamera()
|
||||
{
|
||||
// Vector2.One because Vector2.Zero means no value
|
||||
camera.TargetPos = Vector2.One;
|
||||
}
|
||||
|
||||
void FindInputOuput(CircuitBoxInputOutputNode.Type type)
|
||||
{
|
||||
var input = CircuitBox.InputOutputNodes.FirstOrDefault(n => n.NodeType == type);
|
||||
if (input is null) { return; }
|
||||
|
||||
camera.TargetPos = input.Position;
|
||||
}
|
||||
|
||||
void FindCircuit()
|
||||
{
|
||||
var closestComponent = CircuitBox.Components.MinBy(c => Vector2.DistanceSquared(c.Position, camera.Position));
|
||||
if (closestComponent is null) { return; }
|
||||
|
||||
camera.TargetPos = closestComponent.Position;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
MouseSnapshotHandler.UpdateConnections();
|
||||
|
||||
// update scales of everything
|
||||
foreach (var node in CircuitBox.Components) { node.OnUICreated(); }
|
||||
foreach (var node in CircuitBox.InputOutputNodes) { node.OnUICreated(); }
|
||||
foreach (var wire in CircuitBox.Wires) { wire.Update(); }
|
||||
}
|
||||
|
||||
private string GetInventoryText()
|
||||
=> CircuitBox.ComponentContainer is { } container
|
||||
? $"{container.Inventory.AllItems.Count()}/{container.Capacity}"
|
||||
: "0/0";
|
||||
|
||||
public void UpdateComponentList()
|
||||
{
|
||||
if (inventoryIndicatorText is { } text)
|
||||
{
|
||||
text.Text = GetInventoryText();
|
||||
}
|
||||
|
||||
if (componentList is null) { return; }
|
||||
var playerInventory = CircuitBox.GetSortedCircuitBoxSortedItemsFromPlayer(Character.Controlled);
|
||||
|
||||
foreach (GUIComponent child in componentList.Content.Children)
|
||||
{
|
||||
if (child.UserData is not ItemPrefab prefab) { continue; }
|
||||
|
||||
child.Enabled = !CircuitBox.IsFull && (!CircuitBox.IsInGame() || CircuitBox.GetApplicableResourcePlayerHas(prefab, playerInventory).IsSome());
|
||||
|
||||
if (child.GetChild<GUILayoutGroup>()?.GetChild<GUIImage>() is { } image)
|
||||
{
|
||||
image.Enabled = child.Enabled;
|
||||
}
|
||||
|
||||
child.ToolTip = child.Enabled
|
||||
? prefab.Description
|
||||
: RichString.Rich(TextManager.GetWithVariable(new Identifier("CircuitBoxUIComponentNotAvailable"), new Identifier("[item]"), prefab.Name));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(searchTerm))
|
||||
{
|
||||
child.Visible = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
child.Visible = prefab.Name.Contains(searchTerm, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SelectWire(GUIComponent component, object obj)
|
||||
{
|
||||
if (obj is not ItemPrefab prefab) { return false; }
|
||||
|
||||
CircuitBoxWire.SelectedWirePrefab = prefab;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void CreateComponentElement(ItemPrefab prefab, RectTransform parent)
|
||||
{
|
||||
GUIFrame itemFrame = new GUIFrame(new RectTransform(new Vector2(0.1f, 0.9f), parent) { MinSize = new Point(0, 50) }, style: "GUITextBox")
|
||||
{
|
||||
UserData = prefab
|
||||
};
|
||||
|
||||
itemFrame.RectTransform.MinSize = new Point(0, itemFrame.Rect.Width);
|
||||
itemFrame.RectTransform.MaxSize = new Point(int.MaxValue, itemFrame.Rect.Width);
|
||||
itemFrame.ToolTip = prefab.Name;
|
||||
|
||||
GUILayoutGroup paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), itemFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.03f,
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
Sprite icon;
|
||||
Color iconColor;
|
||||
|
||||
if (prefab.InventoryIcon != null)
|
||||
{
|
||||
icon = prefab.InventoryIcon;
|
||||
iconColor = prefab.InventoryIconColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = prefab.Sprite;
|
||||
iconColor = prefab.SpriteColor;
|
||||
}
|
||||
|
||||
GUIImage? img = null;
|
||||
if (icon != null)
|
||||
{
|
||||
img = new GUIImage(new RectTransform(new Vector2(1.0f, 0.8f), paddedFrame.RectTransform, Anchor.TopCenter), icon)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
LoadAsynchronously = true,
|
||||
DisabledColor = Color.DarkGray * 0.8f,
|
||||
Color = iconColor
|
||||
};
|
||||
}
|
||||
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform, Anchor.BottomCenter),
|
||||
text: prefab.Name, textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
textBlock.Text = ToolBox.LimitString(textBlock.Text, textBlock.Font, textBlock.Rect.Width);
|
||||
paddedFrame.Recalculate();
|
||||
|
||||
if (img != null)
|
||||
{
|
||||
img.Scale = Math.Min(Math.Min(img.Rect.Width / img.Sprite.size.X, img.Rect.Height / img.Sprite.size.Y), 1.5f);
|
||||
img.RectTransform.NonScaledSize = new Point((int)(img.Sprite.size.X * img.Scale), img.Rect.Height);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void DrawHUD(SpriteBatch spriteBatch)
|
||||
{
|
||||
float scale = GUI.Scale / 1.5f;
|
||||
Vector2 offset = new Vector2(20, 40) * scale;
|
||||
|
||||
foreach (var (character, cursor) in CircuitBox.ActiveCursors)
|
||||
{
|
||||
if (!cursor.IsActive) { continue; }
|
||||
|
||||
Vector2 cursorWorldPos = camera.WorldToScreen(cursor.DrawPosition);
|
||||
|
||||
if (cursor.Info.DragStart.TryUnwrap(out Vector2 dragStart))
|
||||
{
|
||||
DrawSelection(spriteBatch, dragStart, cursor.DrawPosition, cursor.Color);
|
||||
}
|
||||
|
||||
if (cursor.HeldPrefab.TryUnwrap(out ItemPrefab? otherHeldPrefab))
|
||||
{
|
||||
otherHeldPrefab.Sprite.Draw(spriteBatch, cursorWorldPos);
|
||||
}
|
||||
|
||||
cursorSprite?.Draw(spriteBatch, cursorWorldPos, cursor.Color, 0f, scale);
|
||||
GUI.DrawString(spriteBatch, cursorWorldPos + offset, character.Name, cursor.Color, Color.Black, GUI.IntScale(4), GUIStyle.SmallFont);
|
||||
}
|
||||
|
||||
if (selection.TryUnwrap(out RectangleF rect))
|
||||
{
|
||||
Vector2 pos1 = rect.Location;
|
||||
Vector2 pos2 = new Vector2(rect.Location.X + rect.Size.X, rect.Location.Y + rect.Size.Y);
|
||||
DrawSelection(spriteBatch, pos1, pos2, GUIStyle.Blue);
|
||||
}
|
||||
|
||||
if (CircuitBox.HeldComponent.TryUnwrap(out ItemPrefab? component))
|
||||
{
|
||||
component.Sprite.Draw(spriteBatch, PlayerInput.MousePosition);
|
||||
}
|
||||
|
||||
foreach (var c in CircuitBox.Components)
|
||||
{
|
||||
c.DrawHUD(spriteBatch, camera);
|
||||
}
|
||||
|
||||
foreach (var n in CircuitBox.InputOutputNodes)
|
||||
{
|
||||
n.DrawHUD(spriteBatch, camera);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSelection(SpriteBatch spriteBatch, Vector2 pos1, Vector2 pos2, Color color)
|
||||
{
|
||||
Vector2 location = camera.WorldToScreen(pos1);
|
||||
location.Y = -location.Y;
|
||||
Vector2 location2 = camera.WorldToScreen(pos2);
|
||||
location2.Y = -location2.Y;
|
||||
MapEntity.DrawSelectionRect(spriteBatch, location, new Vector2(-(location.X - location2.X), location.Y - location2.Y), color);
|
||||
}
|
||||
|
||||
private const float lineBaseWidth = 1f;
|
||||
private static float lineWidth;
|
||||
|
||||
public static void DrawRectangleWithBorder(SpriteBatch spriteBatch, RectangleF rect, Color fillColor, Color borderColor)
|
||||
{
|
||||
Vector2 topRight = new Vector2(rect.Right, rect.Top),
|
||||
topLeft = new Vector2(rect.Left, rect.Top),
|
||||
bottomRight = new Vector2(rect.Right, rect.Bottom),
|
||||
bottomLeft = new Vector2(rect.Left, rect.Bottom);
|
||||
|
||||
Vector2 offset = new Vector2(0f, lineWidth / 2f);
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, rect, fillColor);
|
||||
|
||||
spriteBatch.DrawLine(topRight, topLeft, borderColor, thickness: lineWidth);
|
||||
spriteBatch.DrawLine(topLeft - offset, bottomLeft + offset, borderColor, thickness: lineWidth);
|
||||
spriteBatch.DrawLine(bottomLeft, bottomRight, borderColor, thickness: lineWidth);
|
||||
spriteBatch.DrawLine(bottomRight + offset, topRight - offset, borderColor, thickness: lineWidth);
|
||||
}
|
||||
|
||||
private void DrawCircuits(SpriteBatch spriteBatch)
|
||||
{
|
||||
camera.UpdateTransform(interpolate: true, updateListener: false);
|
||||
SubEditorScreen.DrawOutOfBoundsArea(spriteBatch, camera, CircuitBoxSizes.PlayableAreaSize, GUIStyle.Red * 0.33f);
|
||||
SubEditorScreen.DrawGrid(spriteBatch, camera, gridSize.X, gridSize.Y, zoomTreshold: false);
|
||||
lineWidth = lineBaseWidth / camera.Zoom;
|
||||
|
||||
Vector2 mousePos = GetCursorPosition();
|
||||
mousePos.Y = -mousePos.Y;
|
||||
|
||||
foreach (CircuitBoxWire wire in CircuitBox.Wires)
|
||||
{
|
||||
wire.Renderer.Draw(spriteBatch, GetSelectionColor(wire));
|
||||
}
|
||||
|
||||
foreach (var node in CircuitBox.Components)
|
||||
{
|
||||
if (node.IsSelected)
|
||||
{
|
||||
node.DrawSelection(spriteBatch, GetSelectionColor(node));
|
||||
}
|
||||
|
||||
node.Draw(spriteBatch, node.Position, node.Item.Prefab.SignalComponentColor * CircuitBoxNode.Opacity);
|
||||
}
|
||||
|
||||
foreach (var ioNode in CircuitBox.InputOutputNodes)
|
||||
{
|
||||
if (ioNode.IsSelected)
|
||||
{
|
||||
ioNode.DrawSelection(spriteBatch, GetSelectionColor(ioNode));
|
||||
}
|
||||
|
||||
Color color = ioNode.NodeType is CircuitBoxInputOutputNode.Type.Input ? GUIStyle.Green : GUIStyle.Red;
|
||||
ioNode.Draw(spriteBatch, ioNode.Position, color * CircuitBoxNode.Opacity);
|
||||
}
|
||||
|
||||
if (MouseSnapshotHandler.IsDragging)
|
||||
{
|
||||
var draggedNodes = MouseSnapshotHandler.GetMoveAffectedComponents();
|
||||
Vector2 dragOffset = MouseSnapshotHandler.GetDragAmount(GetCursorPosition());
|
||||
foreach (CircuitBoxNode moveable in draggedNodes)
|
||||
{
|
||||
Color color = moveable switch
|
||||
{
|
||||
CircuitBoxComponent node => node.Item.Prefab.SignalComponentColor,
|
||||
CircuitBoxInputOutputNode ioNode => ioNode.NodeType is CircuitBoxInputOutputNode.Type.Input ? GUIStyle.Green : GUIStyle.Red,
|
||||
_ => Color.White
|
||||
};
|
||||
moveable.Draw(spriteBatch, moveable.Position + dragOffset, color * 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
if (DraggedWire.TryUnwrap(out CircuitBoxWireRenderer? draggedWire))
|
||||
{
|
||||
draggedWire.Draw(spriteBatch, GUIStyle.Yellow);
|
||||
}
|
||||
}
|
||||
|
||||
private Color GetSelectionColor(CircuitBoxNode node)
|
||||
=> GetSelectionColor(node.SelectedBy, node.IsSelectedByMe);
|
||||
|
||||
private Color GetSelectionColor(CircuitBoxWire wire)
|
||||
=> GetSelectionColor(wire.SelectedBy, wire.IsSelectedByMe);
|
||||
|
||||
private Color GetSelectionColor(ushort selectedBy, bool isSelectedByMe)
|
||||
{
|
||||
#if !DEBUG
|
||||
if (isSelectedByMe)
|
||||
{
|
||||
return GUIStyle.Yellow;
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (var (_, cursor) in CircuitBox.ActiveCursors)
|
||||
{
|
||||
if (cursor.Info.CharacterID == selectedBy)
|
||||
{
|
||||
return cursor.Color;
|
||||
}
|
||||
}
|
||||
|
||||
return GUIStyle.Yellow;
|
||||
}
|
||||
|
||||
private Vector2 cursorPos;
|
||||
public Vector2 GetCursorPosition() => cursorPos;
|
||||
public Option<Vector2> GetDragStart() => selection.Select(static f => f.Location);
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
cursorPos = camera.ScreenToWorld(PlayerInput.MousePosition);
|
||||
foreach (CircuitBoxWire wire in CircuitBox.Wires)
|
||||
{
|
||||
wire.Update();
|
||||
}
|
||||
|
||||
bool foundSelected = false;
|
||||
foreach (var node in CircuitBox.Components)
|
||||
{
|
||||
if (!node.IsSelectedByMe) { continue; }
|
||||
|
||||
foundSelected = true;
|
||||
if (circuitComponent is not null)
|
||||
{
|
||||
node.UpdateEditing(circuitComponent.RectTransform);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundSelected)
|
||||
{
|
||||
CircuitBoxComponent.RemoveEditingHUD();
|
||||
}
|
||||
|
||||
bool isMouseOn = GUI.MouseOn == circuitComponent;
|
||||
|
||||
if (isMouseOn)
|
||||
{
|
||||
Character.DisableControls = true;
|
||||
}
|
||||
camera.MoveCamera(deltaTime, allowMove: true, allowZoom: isMouseOn, allowInput: isMouseOn, followSub: false);
|
||||
|
||||
if (camera.TargetPos != Vector2.Zero && MathUtils.NearlyEqual(camera.Position, camera.TargetPos, 0.01f))
|
||||
{
|
||||
camera.TargetPos = Vector2.Zero;
|
||||
}
|
||||
|
||||
if (isMouseOn)
|
||||
{
|
||||
if (CircuitBox.HeldComponent.IsNone() && PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
MouseSnapshotHandler.StartDragging();
|
||||
}
|
||||
|
||||
if (PlayerInput.MidButtonHeld() || (PlayerInput.IsAltDown() && PlayerInput.PrimaryMouseButtonHeld()))
|
||||
{
|
||||
Vector2 moveSpeed = PlayerInput.MouseSpeed / camera.Zoom;
|
||||
moveSpeed.X = -moveSpeed.X;
|
||||
camera.Position += moveSpeed;
|
||||
}
|
||||
|
||||
if (PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
MouseSnapshotHandler.UpdateDrag(GetCursorPosition());
|
||||
}
|
||||
|
||||
if (MouseSnapshotHandler.IsWiring && MouseSnapshotHandler.LastConnectorUnderCursor.TryUnwrap(out var c))
|
||||
{
|
||||
Vector2 start = c.Rect.Center,
|
||||
end = GetCursorPosition();
|
||||
|
||||
end.Y = -end.Y;
|
||||
|
||||
if (!c.IsOutput)
|
||||
{
|
||||
(start, end) = (end, start);
|
||||
}
|
||||
|
||||
if (DraggedWire.TryUnwrap(out var wire))
|
||||
{
|
||||
wire.Recompute(start, end, CircuitBoxWire.SelectedWirePrefab.SpriteColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
DraggedWire = Option.Some(new CircuitBoxWireRenderer(Option.None,start, end, GUIStyle.Red, CircuitBox.WireSprite));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DraggedWire = Option.None;
|
||||
}
|
||||
|
||||
if (PlayerInput.SecondaryMouseButtonClicked())
|
||||
{
|
||||
OpenContextMenu();
|
||||
}
|
||||
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
if (CircuitBox.HeldComponent.TryUnwrap(out ItemPrefab? prefab))
|
||||
{
|
||||
CircuitBox.AddComponent(prefab, cursorPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MouseSnapshotHandler.IsDragging && PlayerInput.PrimaryMouseButtonReleased())
|
||||
{
|
||||
CircuitBox.MoveComponent(MouseSnapshotHandler.GetDragAmount(cursorPos), MouseSnapshotHandler.GetMoveAffectedComponents());
|
||||
}
|
||||
else if (!MouseSnapshotHandler.IsWiring)
|
||||
{
|
||||
TrySelectComponentsUnderCursor();
|
||||
}
|
||||
}
|
||||
|
||||
if (MouseSnapshotHandler.IsWiring && MouseSnapshotHandler.LastConnectorUnderCursor.TryUnwrap(out var one))
|
||||
{
|
||||
if (MouseSnapshotHandler.FindConnectorUnderCursor(cursorPos).TryUnwrap(out var two))
|
||||
{
|
||||
CircuitBox.AddWire(one, two);
|
||||
}
|
||||
}
|
||||
|
||||
CircuitBox.SelectWires(MouseSnapshotHandler.LastWireUnderCursor.TryUnwrap(out var wire) ? ImmutableArray.Create(wire) : ImmutableArray<CircuitBoxWire>.Empty, !PlayerInput.IsShiftDown());
|
||||
|
||||
CircuitBox.HeldComponent = Option.None;
|
||||
MouseSnapshotHandler.EndDragging();
|
||||
}
|
||||
|
||||
if (MouseSnapshotHandler.GetLastComponentsUnderCursor().IsEmpty && MouseSnapshotHandler.LastConnectorUnderCursor.IsNone())
|
||||
{
|
||||
UpdateSelection();
|
||||
}
|
||||
|
||||
// Allow using both Delete key and Ctrl+D for those who don't have a Delete key
|
||||
bool hitDeleteCombo = PlayerInput.KeyHit(Keys.Delete) || (PlayerInput.IsCtrlDown() && PlayerInput.KeyHit(Keys.D));
|
||||
|
||||
if (GUI.KeyboardDispatcher.Subscriber is null && hitDeleteCombo)
|
||||
{
|
||||
CircuitBox.RemoveComponents(CircuitBox.Components.Where(static node => node.IsSelectedByMe).ToArray());
|
||||
CircuitBox.RemoveWires(CircuitBox.Wires.Where(static wire => wire.IsSelectedByMe).ToImmutableArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (componentMenu is { } menu && toggleMenuButton is { } button)
|
||||
{
|
||||
componentMenuOpenState = componentMenuOpen ? Math.Min(componentMenuOpenState + deltaTime * 5.0f, 1.0f) : Math.Max(componentMenuOpenState - deltaTime * 5.0f, 0.0f);
|
||||
|
||||
menu.RectTransform.ScreenSpaceOffset = Vector2.Lerp(new Vector2(0.0f, menu.Rect.Height - 10), Vector2.Zero, componentMenuOpenState).ToPoint();
|
||||
button.RectTransform.AbsoluteOffset = new Point(menu.Rect.X + ((menu.Rect.Width / 2) - (button.Rect.Width / 2)), menu.Rect.Y - button.Rect.Height);
|
||||
}
|
||||
|
||||
camera.Position = Vector2.Clamp(camera.Position,
|
||||
new Vector2(-CircuitBoxSizes.PlayableAreaSize / 2f),
|
||||
new Vector2(CircuitBoxSizes.PlayableAreaSize / 2f));
|
||||
}
|
||||
|
||||
private void UpdateSelection()
|
||||
{
|
||||
if (!PlayerInput.IsAltDown() && PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
selection = Option.Some(new RectangleF(GetCursorPosition(), Vector2.Zero));
|
||||
}
|
||||
|
||||
if (!selection.TryUnwrap(out RectangleF rect)) { return; }
|
||||
|
||||
if (!PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
selection = Option.None;
|
||||
RectangleF selectionRect = Submarine.AbsRectF(rect.Location, rect.Size);
|
||||
|
||||
float treshold = 12f / camera.Zoom;
|
||||
if (selectionRect.Size.X < treshold || selectionRect.Size.Y < treshold) { return; }
|
||||
|
||||
CircuitBox.SelectComponents(MouseSnapshotHandler.Nodes.Where(n => selectionRect.Intersects(n.Rect)).ToImmutableHashSet(), !PlayerInput.IsShiftDown());
|
||||
}
|
||||
else
|
||||
{
|
||||
RectangleF oldRect = rect;
|
||||
rect.Size = camera.ScreenToWorld(PlayerInput.MousePosition) - rect.Location;
|
||||
if (rect.Equals(oldRect)) { return; }
|
||||
|
||||
selection = Option.Some(rect);
|
||||
}
|
||||
}
|
||||
|
||||
private void TrySelectComponentsUnderCursor()
|
||||
{
|
||||
CircuitBoxNode? foundNode = GetTopmostNode(MouseSnapshotHandler.GetLastComponentsUnderCursor());
|
||||
|
||||
CircuitBox.SelectComponents(foundNode is null ? ImmutableArray<CircuitBoxNode>.Empty : ImmutableArray.Create(foundNode), !PlayerInput.IsShiftDown());
|
||||
}
|
||||
|
||||
private void OpenContextMenu()
|
||||
{
|
||||
var wireOption = MouseSnapshotHandler.FindWireUnderCursor(cursorPos);
|
||||
var wireSelection = CircuitBox.Wires.Where(static w => w.IsSelectedByMe).ToImmutableArray();
|
||||
var nodeOption = GetTopmostNode(MouseSnapshotHandler.FindNodesUnderCursor(cursorPos));
|
||||
var nodeSelection = CircuitBox.Components.Where(static n => n.IsSelectedByMe).ToImmutableArray();
|
||||
|
||||
var option = new ContextMenuOption(TextManager.Get("delete"), isEnabled: wireOption.IsSome() || nodeOption is CircuitBoxComponent, () =>
|
||||
{
|
||||
if (wireOption.TryUnwrap(out var wire))
|
||||
{
|
||||
CircuitBox.RemoveWires(wire.IsSelected ? wireSelection : ImmutableArray.Create(wire));
|
||||
}
|
||||
|
||||
if (nodeOption is CircuitBoxComponent node)
|
||||
{
|
||||
CircuitBox.RemoveComponents(node.IsSelected ? nodeSelection : ImmutableArray.Create(node));
|
||||
}
|
||||
});
|
||||
|
||||
// show component name in the header to better indicate what is about to be deleted
|
||||
if (nodeOption is CircuitBoxComponent comp)
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu(PlayerInput.MousePosition, comp.Item.Name, comp.Item.Prefab.SignalComponentColor, option);
|
||||
return;
|
||||
}
|
||||
|
||||
// also check if a wire is being deleted
|
||||
if (wireOption.TryUnwrap(out var foundWire))
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu(PlayerInput.MousePosition, foundWire.UsedItemPrefab.Name, foundWire.Color, option);
|
||||
return;
|
||||
}
|
||||
|
||||
GUIContextMenu.CreateContextMenu(option);
|
||||
}
|
||||
|
||||
public CircuitBoxNode? GetTopmostNode(ImmutableHashSet<CircuitBoxNode> nodes)
|
||||
{
|
||||
CircuitBoxNode? foundNode = null;
|
||||
|
||||
var allNodes = MouseSnapshotHandler.Nodes.ToImmutableArray();
|
||||
|
||||
for (int i = allNodes.Length - 1; i >= 0; i--)
|
||||
{
|
||||
CircuitBoxNode node = allNodes[i];
|
||||
|
||||
if (nodes.Contains(node))
|
||||
{
|
||||
foundNode = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return foundNode;
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
toggleMenuButton?.AddToGUIUpdateList();
|
||||
selectedWireFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class CircuitBoxWire
|
||||
{
|
||||
public CircuitBoxWireRenderer Renderer;
|
||||
|
||||
public void Update() => Renderer.Recompute(From.AnchorPoint, To.AnchorPoint, Color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal class CircuitBoxWireRenderer
|
||||
{
|
||||
private const int VertsPerQuad = 4, // how many points per quad
|
||||
QuadsPerLine = 10, // how many quads per line
|
||||
VertsPerLine = QuadsPerLine * VertsPerQuad, // how many points we need to draw all the quads for a single line
|
||||
TotalVertsPerWire = VertsPerLine * 2; // we are drawing 2 lines
|
||||
|
||||
private readonly Texture2D texture;
|
||||
|
||||
private VertexPositionColorTexture[] verts = new VertexPositionColorTexture[TotalVertsPerWire];
|
||||
private readonly Vector2[][] colliders = new Vector2[2][];
|
||||
private SquareLine skeleton;
|
||||
|
||||
private Vector2 lastStart, lastEnd;
|
||||
private Color lastColor;
|
||||
private readonly Option<CircuitBoxWire> wire;
|
||||
|
||||
public CircuitBoxWireRenderer(Option<CircuitBoxWire> wire, Vector2 start, Vector2 end, Color color, Sprite? wireSprite)
|
||||
{
|
||||
this.wire = wire;
|
||||
texture = wireSprite?.Texture ?? GUI.WhiteTexture;
|
||||
Recompute(start, end, color);
|
||||
}
|
||||
|
||||
private void UpdateColor(Color color)
|
||||
{
|
||||
for (int i = 0; i < TotalVertsPerWire; i++)
|
||||
{
|
||||
verts[i].Color = color;
|
||||
}
|
||||
|
||||
lastColor = color;
|
||||
}
|
||||
|
||||
public void Recompute(Vector2 start, Vector2 end, Color color)
|
||||
{
|
||||
if (MathUtils.NearlyEqual(lastStart, start) && MathUtils.NearlyEqual(lastEnd, end))
|
||||
{
|
||||
if (lastColor == color) { return; }
|
||||
|
||||
UpdateColor(color);
|
||||
return;
|
||||
}
|
||||
|
||||
lastStart = start;
|
||||
lastEnd = end;
|
||||
lastColor = color;
|
||||
|
||||
skeleton = ToolBox.GetSquareLineBetweenPoints(start, end, CircuitBoxSizes.WireKnobLength);
|
||||
var points = skeleton.Points;
|
||||
|
||||
Vector2 centerOfLine = (points[2] + points[3]) / 2f;
|
||||
|
||||
ImmutableArray<Vector2> points1 = GetLinePoints(points[1], points[2], centerOfLine),
|
||||
points2 = GetLinePoints(centerOfLine, points[3], points[4]);
|
||||
|
||||
colliders[0] = ConstructQuads(ref verts, 0, points1, color);
|
||||
colliders[1] = ConstructQuads(ref verts, VertsPerLine, points2, color);
|
||||
|
||||
static ImmutableArray<Vector2> GetLinePoints(Vector2 start, Vector2 control, Vector2 end)
|
||||
{
|
||||
var points = ImmutableArray.CreateBuilder<Vector2>(QuadsPerLine);
|
||||
for (int i = 0; i < QuadsPerLine; i++)
|
||||
{
|
||||
float t = (float)i / (QuadsPerLine - 1);
|
||||
Vector2 pos = MathUtils.Bezier(start, control, end, t);
|
||||
points.Add(pos);
|
||||
}
|
||||
|
||||
return points.ToImmutable();
|
||||
}
|
||||
|
||||
static Vector2[] ConstructQuads(ref VertexPositionColorTexture[] verts, int startOffset, IReadOnlyList<Vector2> points, Color color)
|
||||
{
|
||||
// ok I don't know why this needs to be one quad less, maybe we are drawing with only 9 quads lol
|
||||
var collider = new Vector2[VertsPerLine - VertsPerQuad];
|
||||
|
||||
int leftIndex = collider.Length - 1,
|
||||
rightIndex = 0;
|
||||
|
||||
// we need to calculate half of the width since the way we expand the quads from origin, otherwise the line will be twice as wide
|
||||
const float halfWidth = CircuitBoxSizes.WireWidth / 2f;
|
||||
|
||||
// draw the line using quads
|
||||
for (int i = 0; i < points.Count - 1; i++)
|
||||
{
|
||||
bool isFirst = i == 0 && startOffset == 0,
|
||||
isLast = i == points.Count - 2 && startOffset > 0;
|
||||
|
||||
Vector2 start = points[i],
|
||||
end = points[i + 1];
|
||||
|
||||
Vector2 dir = Vector2.Normalize(end - start);
|
||||
Vector2 length = new Vector2(dir.Y, -dir.X) * halfWidth;
|
||||
|
||||
int vertIndex = startOffset + i * 4;
|
||||
|
||||
Vector2 topRight = end + length;
|
||||
Vector2 topLeft = end - length;
|
||||
|
||||
Vector2 bottomRight;
|
||||
Vector2 bottomLeft;
|
||||
|
||||
// get previous points if any
|
||||
int prevIndex = vertIndex - 4;
|
||||
|
||||
if ((prevIndex - startOffset) >= 0)
|
||||
{
|
||||
// connect the previous "upper" corners into the current "lower" corners to stitch the line together
|
||||
Vector3 prevTopRight = verts[TopRight(prevIndex)].Position,
|
||||
prevTopLeft = verts[TopLeft(prevIndex)].Position;
|
||||
|
||||
bottomRight = ToVector2(prevTopRight);
|
||||
bottomLeft = ToVector2(prevTopLeft);
|
||||
}
|
||||
else
|
||||
{
|
||||
bottomRight = start + length;
|
||||
bottomLeft = start - length;
|
||||
}
|
||||
|
||||
if (isFirst)
|
||||
{
|
||||
if (MathF.Abs(dir.Y) > MathF.Abs(dir.X))
|
||||
{
|
||||
float offset = dir.Y < 0 ? halfWidth : -halfWidth;
|
||||
// if the line is more vertical than horizontal, we want to move the bottom corners to the left
|
||||
bottomRight.Y = start.Y - offset;
|
||||
bottomLeft.Y = start.Y - offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise we want to move the bottom corners to the top
|
||||
bottomRight.X = start.X;
|
||||
bottomLeft.X = start.X;
|
||||
}
|
||||
}
|
||||
else if (isLast)
|
||||
{
|
||||
if (MathF.Abs(dir.Y) > MathF.Abs(dir.X))
|
||||
{
|
||||
float offset = dir.Y < 0 ? halfWidth : -halfWidth;
|
||||
// if the line is more vertical than horizontal, we want to move the bottom corners to the left
|
||||
topRight.Y = end.Y + offset;
|
||||
topLeft.Y = end.Y + offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise we want to move the bottom corners to the top
|
||||
topRight.X = end.X;
|
||||
topLeft.X = end.X;
|
||||
}
|
||||
}
|
||||
|
||||
collider[rightIndex++] = bottomLeft;
|
||||
collider[rightIndex++] = topLeft;
|
||||
|
||||
collider[leftIndex--] = bottomRight;
|
||||
collider[leftIndex--] = topRight;
|
||||
|
||||
// adjust this if we want sprites to support sourceRects
|
||||
Vector2 uvTopRight = new Vector2(0, 1),
|
||||
uvTopLeft = new Vector2(0, 0),
|
||||
uvBottomRight = new Vector2(1, 1),
|
||||
uvBottomLeft = new Vector2(1, 0);
|
||||
|
||||
SetPos(ref verts, TopRight(vertIndex), topRight, color, uvTopRight);
|
||||
SetPos(ref verts, TopLeft(vertIndex), topLeft, color, uvTopLeft);
|
||||
SetPos(ref verts, BottomRight(vertIndex), bottomRight, color, uvBottomRight);
|
||||
SetPos(ref verts, BottomLeft(vertIndex), bottomLeft, color, uvBottomLeft);
|
||||
|
||||
static void SetPos(ref VertexPositionColorTexture[] verts, int index, Vector2 pos, Color color, Vector2 uv)
|
||||
{
|
||||
verts[index].Position = ToVector3(pos);
|
||||
verts[index].Color = color;
|
||||
verts[index].TextureCoordinate = uv;
|
||||
static Vector3 ToVector3(Vector2 v) => new Vector3(v.X, v.Y, 0f);
|
||||
}
|
||||
|
||||
static int TopRight(int vertIndex) => vertIndex;
|
||||
static int TopLeft(int vertIndex) => vertIndex + 1;
|
||||
static int BottomRight(int vertIndex) => vertIndex + 2;
|
||||
static int BottomLeft(int vertIndex) => vertIndex + 3;
|
||||
|
||||
static Vector2 ToVector2(Vector3 v) => new Vector2(v.X, v.Y);
|
||||
}
|
||||
|
||||
return collider;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(Vector2 pos)
|
||||
{
|
||||
pos.Y = -pos.Y;
|
||||
foreach (Vector2[] collider in colliders)
|
||||
{
|
||||
if (ToolBox.PointIntersectsWithPolygon(pos, collider, checkBoundingBox: false)) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Color selectionColor)
|
||||
{
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
for (int i = 0; i < skeleton.Points.Length; i++)
|
||||
{
|
||||
Vector2 point = skeleton.Points[i];
|
||||
spriteBatch.DrawPoint(point, Color.White, 25f);
|
||||
GUI.DrawString(spriteBatch, point - new Vector2(5f, 17f), i.ToString(), Color.Black, font: GUIStyle.LargeFont);
|
||||
}
|
||||
|
||||
spriteBatch.DrawLine(skeleton.Points[0], skeleton.Points[1], GUIStyle.Green, thickness: 2f);
|
||||
spriteBatch.DrawLine(skeleton.Points[1], skeleton.Points[2], GUIStyle.Green, thickness: 2f);
|
||||
spriteBatch.DrawLine(skeleton.Points[2], skeleton.Points[3], GUIStyle.Green, thickness: 2f);
|
||||
spriteBatch.DrawLine(skeleton.Points[3], skeleton.Points[4], GUIStyle.Green, thickness: 2f);
|
||||
spriteBatch.DrawLine(skeleton.Points[4], skeleton.Points[5], GUIStyle.Green, thickness: 2f);
|
||||
}
|
||||
|
||||
bool isSelected = wire.TryUnwrap(out var w) && w.IsSelected;
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
foreach (var colliderPolys in colliders)
|
||||
{
|
||||
spriteBatch.DrawPolygon(Vector2.Zero, colliderPolys, selectionColor, 5f);
|
||||
}
|
||||
}
|
||||
|
||||
spriteBatch.Draw(texture, verts, 0f);
|
||||
|
||||
if (skeleton.Type is SquareLine.LineType.SixPointBackwardsLine)
|
||||
{
|
||||
// we need to expand the start and end points to make the line look like it's connected to the "smooth" part of the line
|
||||
Vector2 expandedEnd = skeleton.Points[1],
|
||||
expandedStart = skeleton.Points[4];
|
||||
|
||||
expandedEnd.X += CircuitBoxSizes.WireWidth / 2f;
|
||||
expandedStart.X -= CircuitBoxSizes.WireWidth / 2f;
|
||||
|
||||
spriteBatch.DrawLineWithTexture(texture, skeleton.Points[0], expandedEnd, lastColor, thickness: CircuitBoxSizes.WireWidth);
|
||||
spriteBatch.DrawLineWithTexture(texture, expandedStart, skeleton.Points[5], lastColor, thickness: CircuitBoxSizes.WireWidth);
|
||||
|
||||
const float rectSize = CircuitBoxSizes.WireWidth * 1.5f;
|
||||
RectangleF startKnob = new RectangleF(skeleton.Points[1] - new Vector2(rectSize / 2f), new Vector2(rectSize)),
|
||||
endKnob = new RectangleF(skeleton.Points[4] - new Vector2(rectSize / 2f), new Vector2(rectSize));
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, startKnob, lastColor);
|
||||
GUI.DrawFilledRectangle(spriteBatch, endKnob, lastColor);
|
||||
}
|
||||
|
||||
if (!GameMain.DebugDraw) { return; }
|
||||
|
||||
foreach (var colliderPolys in colliders)
|
||||
{
|
||||
spriteBatch.DrawPolygonInner(Vector2.Zero, colliderPolys, Color.Lime, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user