Unstable 1.1.14.0
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class CircuitBoxComponent : CircuitBoxNode, ICircuitBoxIdentifiable
|
||||
{
|
||||
public readonly Item Item;
|
||||
public ushort ID { get; }
|
||||
|
||||
public readonly ItemPrefab UsedResource;
|
||||
|
||||
public CircuitBoxComponent(ushort id, Item item, Vector2 position, CircuitBox circuitBox, ItemPrefab usedResource): base(circuitBox)
|
||||
{
|
||||
if (item.Connections is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item.Connections), $"Tried to load a CircuitBoxNode with an item \"{item.Prefab.Name}\" that has no connections.");
|
||||
}
|
||||
|
||||
var conns = item.Connections.Select(connection => new CircuitBoxNodeConnection(Vector2.Zero, this, connection, circuitBox)).ToList();
|
||||
|
||||
Vector2 size = CalculateSize(conns);
|
||||
|
||||
ID = id;
|
||||
Item = item;
|
||||
Size = size;
|
||||
Connectors = conns.Cast<CircuitBoxConnection>().ToImmutableArray();
|
||||
Position = position;
|
||||
UsedResource = usedResource;
|
||||
UpdatePositions();
|
||||
}
|
||||
|
||||
public static Option<CircuitBoxComponent> TryLoadFromXML(ContentXElement element, CircuitBox circuitBox)
|
||||
{
|
||||
ushort id = element.GetAttributeUInt16("id", ICircuitBoxIdentifiable.NullComponentID);
|
||||
Vector2 position = element.GetAttributeVector2("position", Vector2.Zero);
|
||||
var itemIdOption = ItemSlotIndexPair.TryDeserializeFromXML(element, "backingitemid");
|
||||
Identifier usedResourceIdentifier = element.GetAttributeIdentifier("usedresource", Identifier.Empty);
|
||||
|
||||
if (!itemIdOption.TryUnwrap(out var itemId) || itemId.FindItemInContainer(circuitBox.ComponentContainer) is not { } backingItem)
|
||||
{
|
||||
DebugConsole.ThrowErrorAndLogToGA("CircuitBoxComponent.TryLoadFromXML:IdNotFound",
|
||||
$"Failed to find item with ID {itemId} for CircuitBoxNode with ID {id}");
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
if (!ItemPrefab.Prefabs.TryGet(usedResourceIdentifier, out var usedResource))
|
||||
{
|
||||
DebugConsole.ThrowErrorAndLogToGA("CircuitBoxComponent.TryLoadXML:UsedResourceNotFound",
|
||||
$"Failed to find item prefab with identifier {usedResourceIdentifier} for CircuitBoxNode with ID {id}");
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
return Option.Some(new CircuitBoxComponent(id, backingItem, position, circuitBox, usedResource));
|
||||
}
|
||||
|
||||
public XElement Save()
|
||||
{
|
||||
return new XElement("Component",
|
||||
new XAttribute("id", ID),
|
||||
new XAttribute("position", XMLExtensions.Vector2ToString(Position)),
|
||||
new XAttribute("backingitemid", ItemSlotIndexPair.Serialize(Item)),
|
||||
new XAttribute("usedresource", UsedResource.Identifier));
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
if (Entity.Spawner is { } spawner && Screen.Selected is not { IsEditor: true })
|
||||
{
|
||||
spawner.AddEntityToRemoveQueue(Item);
|
||||
return;
|
||||
}
|
||||
|
||||
// if EntitySpawner is not available
|
||||
Item.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
|
||||
internal class CircuitBoxInputConnection : CircuitBoxConnection
|
||||
{
|
||||
/// <summary>
|
||||
/// Circuit box input connection is classified as an output because it behaves like an output inside the circuit box.
|
||||
/// As in you can connect it to a input pin of a component.
|
||||
/// </summary>
|
||||
public override bool IsOutput => true;
|
||||
public readonly List<CircuitBoxConnection> ExternallyConnectedTo = new();
|
||||
|
||||
public CircuitBoxInputConnection(Vector2 position, Connection connection, CircuitBox box) : base(position, connection, box) { }
|
||||
|
||||
public override void ReceiveSignal(Signal signal)
|
||||
{
|
||||
foreach (CircuitBoxConnection connector in ExternallyConnectedTo)
|
||||
{
|
||||
if (connector is CircuitBoxOutputConnection output)
|
||||
{
|
||||
output.ReceiveSignal(signal);
|
||||
return;
|
||||
}
|
||||
Connection.SendSignalIntoConnection(signal, connector.Connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CircuitBoxOutputConnection : CircuitBoxConnection
|
||||
{
|
||||
/// <summary>
|
||||
/// Circuit box output connection is classified as an input because it behaves like an input inside the circuit box.
|
||||
/// As in you can connect it to a output pin of a component.
|
||||
/// </summary>
|
||||
public override bool IsOutput => false;
|
||||
|
||||
public CircuitBoxOutputConnection(Vector2 position, Connection connection, CircuitBox circuitBox) : base(position, connection, circuitBox) { }
|
||||
|
||||
public override void ReceiveSignal(Signal signal) => CircuitBox.Item.SendSignal(signal, Connection);
|
||||
}
|
||||
|
||||
internal class CircuitBoxNodeConnection : CircuitBoxConnection
|
||||
{
|
||||
public override bool IsOutput => Connection.IsOutput;
|
||||
|
||||
public CircuitBoxComponent Component;
|
||||
|
||||
public bool HasAvailableSlots => Connection.WireSlotsAvailable();
|
||||
|
||||
public CircuitBoxNodeConnection(Vector2 position, CircuitBoxComponent component, Connection connection, CircuitBox circuitBox) : base(position, connection, circuitBox)
|
||||
{
|
||||
Component = component;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(Signal signal) => Connection.SendSignalIntoConnection(signal, Connection);
|
||||
}
|
||||
|
||||
internal abstract partial class CircuitBoxConnection
|
||||
{
|
||||
public readonly Connection Connection;
|
||||
|
||||
public abstract bool IsOutput { get; }
|
||||
|
||||
public RectangleF Rect;
|
||||
|
||||
private Vector2 position;
|
||||
|
||||
public List<CircuitBoxConnection> ExternallyConnectedFrom = new();
|
||||
|
||||
public static float Size = CircuitBoxSizes.ConnectorSize;
|
||||
|
||||
public Vector2 Position
|
||||
{
|
||||
get => position;
|
||||
set
|
||||
{
|
||||
Rect.X = value.X - Rect.Width / 2f;
|
||||
Rect.Y = value.Y - Rect.Height / 2f;
|
||||
position = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float Length { get; private set; }
|
||||
|
||||
public Vector2 AnchorPoint
|
||||
=> new Vector2(IsOutput ? Rect.Right + CircuitBoxSizes.AnchorOffset : Rect.Left - CircuitBoxSizes.AnchorOffset, Rect.Center.Y);
|
||||
|
||||
public readonly CircuitBox CircuitBox;
|
||||
|
||||
protected CircuitBoxConnection(Vector2 position, Connection connection, CircuitBox circuitBox)
|
||||
{
|
||||
Connection = connection;
|
||||
Rect.Width = Rect.Height = Size;
|
||||
Position = position;
|
||||
CircuitBox = circuitBox;
|
||||
InitProjSpecific(circuitBox);
|
||||
}
|
||||
|
||||
private partial void InitProjSpecific(CircuitBox circuitBox);
|
||||
|
||||
public abstract void ReceiveSignal(Signal signal);
|
||||
|
||||
public bool Contains(Vector2 pos)
|
||||
{
|
||||
float x = Rect.X,
|
||||
y = -(Rect.Y + Rect.Height),
|
||||
width = Rect.Width,
|
||||
height = Rect.Height;
|
||||
|
||||
RectangleF rect = new(x, y, width, height);
|
||||
|
||||
return rect.Contains(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct NetCircuitBoxCursorInfo(Vector2[] RecordedPositions, Option<Vector2> DragStart, Option<Identifier> HeldItem, ushort CharacterID = 0) : INetSerializableStruct;
|
||||
|
||||
internal sealed class CircuitBoxCursor
|
||||
{
|
||||
public NetCircuitBoxCursorInfo Info;
|
||||
|
||||
public CircuitBoxCursor(NetCircuitBoxCursorInfo info)
|
||||
{
|
||||
if (Entity.FindEntityByID(info.CharacterID) is Character c)
|
||||
{
|
||||
Color = GenerateColor(c.Name);
|
||||
}
|
||||
|
||||
UpdateInfo(info);
|
||||
}
|
||||
|
||||
public void UpdateInfo(NetCircuitBoxCursorInfo newInfo)
|
||||
{
|
||||
Info = newInfo;
|
||||
|
||||
newInfo.HeldItem.Match(
|
||||
some: newIdentifier =>
|
||||
HeldPrefab.Match(
|
||||
some: oldPrefab =>
|
||||
{
|
||||
if (oldPrefab.Identifier == newIdentifier) { return; }
|
||||
SetHeldPrefab(newIdentifier);
|
||||
},
|
||||
none: () => SetHeldPrefab(newIdentifier)
|
||||
),
|
||||
none: () => HeldPrefab = Option.None);
|
||||
|
||||
prevPosition = DrawPosition;
|
||||
|
||||
void SetHeldPrefab(Identifier identifier)
|
||||
{
|
||||
ItemPrefab? prefab = ItemPrefab.Prefabs.Find(prefab => prefab.Identifier.Equals(identifier));
|
||||
HeldPrefab = prefab is null ? Option.None : Option.Some(prefab);
|
||||
}
|
||||
}
|
||||
|
||||
public Option<ItemPrefab> HeldPrefab { get; private set; } = Option.None;
|
||||
|
||||
public Color Color = Color.White;
|
||||
|
||||
public static Color GenerateColor(string name)
|
||||
{
|
||||
Random random = new Random(ToolBox.StringToInt(name));
|
||||
return ToolBox.HSVToRGB(random.NextSingle() * 360f, 1f, 1f);
|
||||
}
|
||||
|
||||
private const float UpdateTimeout = 5f;
|
||||
|
||||
private float updateTimer;
|
||||
private float positionTimer;
|
||||
private Vector2 prevPosition;
|
||||
public Vector2 DrawPosition;
|
||||
|
||||
public bool IsActive => updateTimer < UpdateTimeout;
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
updateTimer += deltaTime;
|
||||
|
||||
Vector2 finalPosition = Info.RecordedPositions[^1];
|
||||
|
||||
if (positionTimer > 1f)
|
||||
{
|
||||
DrawPosition = finalPosition;
|
||||
prevPosition = Vector2.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
positionTimer += deltaTime;
|
||||
|
||||
float stepTimer = positionTimer * 10f;
|
||||
int targetPositonIndex = (int)MathF.Floor(stepTimer);
|
||||
int prevPosIndex = targetPositonIndex - 1;
|
||||
|
||||
Vector2 targetPosition = IsInRange(targetPositonIndex, Info.RecordedPositions.Length)
|
||||
? Info.RecordedPositions[targetPositonIndex]
|
||||
: finalPosition;
|
||||
|
||||
Vector2 prevTargetPosition = IsInRange(prevPosIndex, Info.RecordedPositions.Length)
|
||||
? Info.RecordedPositions[prevPosIndex]
|
||||
: prevPosition;
|
||||
|
||||
DrawPosition = Vector2.Lerp(prevTargetPosition, targetPosition, MathHelper.Clamp(stepTimer % 1f, 0f, 1f));
|
||||
}
|
||||
|
||||
static bool IsInRange(int index, int length)
|
||||
=> index >= 0 && index < length;
|
||||
}
|
||||
|
||||
public void ResetTimers()
|
||||
{
|
||||
positionTimer = 0f;
|
||||
updateTimer = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal sealed class CircuitBoxInputOutputNode : CircuitBoxNode
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
Invalid,
|
||||
Input,
|
||||
Output
|
||||
}
|
||||
|
||||
public Type NodeType;
|
||||
|
||||
public CircuitBoxInputOutputNode(IReadOnlyList<CircuitBoxConnection> conns, Vector2 initialPosition, Type type, CircuitBox circuitBox): base(circuitBox)
|
||||
{
|
||||
Size = CalculateSize(conns);
|
||||
Connectors = conns.ToImmutableArray();
|
||||
Position = initialPosition;
|
||||
NodeType = type;
|
||||
UpdatePositions();
|
||||
}
|
||||
|
||||
public XElement Save() => new XElement($"{NodeType}Node", new XAttribute("pos", XMLExtensions.Vector2ToString(Position)));
|
||||
|
||||
public void Load(ContentXElement element)
|
||||
{
|
||||
Position = element.GetAttributeVector2("pos", Vector2.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public enum CircuitBoxOpcode
|
||||
{
|
||||
Error,
|
||||
Cursor,
|
||||
AddComponent,
|
||||
MoveComponent,
|
||||
AddWire,
|
||||
RemoveWire,
|
||||
SelectComponents,
|
||||
SelectWires,
|
||||
UpdateSelection,
|
||||
DeleteComponent,
|
||||
ServerInitialize
|
||||
}
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct NetCircuitBoxHeader(CircuitBoxOpcode Opcode, ushort ItemID, byte ComponentIndex) : INetSerializableStruct
|
||||
{
|
||||
public Option<CircuitBox> FindTarget() => CircuitBox.FindCircuitBox(ItemID, ComponentIndex);
|
||||
}
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxConnectorIdentifier(Identifier SignalConnection, Option<ushort> TargetId) : INetSerializableStruct
|
||||
{
|
||||
public static CircuitBoxConnectorIdentifier FromConnection(CircuitBoxConnection connection) =>
|
||||
connection switch
|
||||
{
|
||||
(CircuitBoxInputConnection or CircuitBoxOutputConnection)
|
||||
=> new CircuitBoxConnectorIdentifier(connection.Name.ToIdentifier(), Option.None),
|
||||
|
||||
CircuitBoxNodeConnection nodeConnection
|
||||
=> new CircuitBoxConnectorIdentifier(connection.Name.ToIdentifier(), Option.Some(nodeConnection.Component.ID)),
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(connection))
|
||||
};
|
||||
|
||||
public Option<CircuitBoxConnection> FindConnection(CircuitBox circuitBox)
|
||||
{
|
||||
if (!TargetId.TryUnwrap(out var id))
|
||||
{
|
||||
return circuitBox.FindInputOutputConnection(SignalConnection);
|
||||
}
|
||||
|
||||
foreach (CircuitBoxComponent boxNode in circuitBox.Components)
|
||||
{
|
||||
if (boxNode.ID != id) { continue; }
|
||||
|
||||
foreach (var conn in boxNode.Connectors)
|
||||
{
|
||||
if (conn.Name != SignalConnection) { continue; }
|
||||
|
||||
return Option.Some(conn);
|
||||
}
|
||||
}
|
||||
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
public XElement Save(string name) => new XElement(name,
|
||||
new XAttribute("name", SignalConnection),
|
||||
new XAttribute("target", TargetId.TryUnwrap(out var value) ? value.ToString() : string.Empty));
|
||||
|
||||
public static CircuitBoxConnectorIdentifier Load(ContentXElement element)
|
||||
{
|
||||
string? name = element.GetAttributeString("name", string.Empty);
|
||||
string? target = element.GetAttributeString("target", string.Empty);
|
||||
|
||||
Option<ushort> targetId = Option.None;
|
||||
if (!string.IsNullOrWhiteSpace(target))
|
||||
{
|
||||
targetId = ushort.TryParse(target, out var value) ? Option.Some(value) : Option.None;
|
||||
}
|
||||
|
||||
return new CircuitBoxConnectorIdentifier(name.ToIdentifier(), targetId);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> $"{{Name: {SignalConnection}, ID: {(TargetId.TryUnwrap(out var value) ? value.ToString() : "N/A")}}}";
|
||||
}
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxAddComponentEvent(UInt32 PrefabIdentifier, Vector2 Position) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxServerCreateComponentEvent(ushort BackingItemId, UInt32 UsedResource, ushort ComponentId, Vector2 Position) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxRemoveComponentEvent(ImmutableArray<ushort> TargetIDs) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxMoveComponentEvent(ImmutableArray<ushort> TargetIDs, ImmutableArray<CircuitBoxInputOutputNode.Type> IOs, Vector2 MoveAmount) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxSelectNodesEvent(ImmutableArray<ushort> TargetIDs, ImmutableArray<CircuitBoxInputOutputNode.Type> IOs, bool Overwrite, ushort CharacterID) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxServerUpdateSelection(ImmutableArray<CircuitBoxIdSelectionPair> ComponentIds, ImmutableArray<CircuitBoxIdSelectionPair> WireIds, ImmutableArray<CircuitBoxTypeSelectionPair> InputOutputs) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxIdSelectionPair(ushort ID, Option<ushort> SelectedBy) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxTypeSelectionPair(CircuitBoxInputOutputNode.Type Type, Option<ushort> SelectedBy) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxSelectWiresEvent(ImmutableArray<ushort> TargetIDs, bool Overwrite, ushort CharacterID) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxClientAddWireEvent(Color Color, CircuitBoxConnectorIdentifier Start, CircuitBoxConnectorIdentifier End, UInt32 SelectedWirePrefabIdentifier) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxServerCreateWireEvent(CircuitBoxClientAddWireEvent Request, ushort WireId, Option<ushort> BackingItemId) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxRemoveWireEvent(ImmutableArray<ushort> TargetIDs) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxErrorEvent(string Message) : INetSerializableStruct;
|
||||
|
||||
[NetworkSerialize]
|
||||
internal readonly record struct CircuitBoxInitializeStateFromServerEvent(
|
||||
ImmutableArray<CircuitBoxServerCreateComponentEvent> Components,
|
||||
ImmutableArray<CircuitBoxServerCreateWireEvent> Wires,
|
||||
Vector2 InputPos,
|
||||
Vector2 OutputPos) : INetSerializableStruct;
|
||||
|
||||
internal readonly record struct CircuitBoxEventData(INetSerializableStruct Data) : ItemComponent.IEventData
|
||||
{
|
||||
public CircuitBoxOpcode Opcode =>
|
||||
Data switch
|
||||
{
|
||||
(CircuitBoxAddComponentEvent or CircuitBoxServerCreateComponentEvent)
|
||||
=> CircuitBoxOpcode.AddComponent,
|
||||
CircuitBoxRemoveComponentEvent
|
||||
=> CircuitBoxOpcode.DeleteComponent,
|
||||
CircuitBoxMoveComponentEvent
|
||||
=> CircuitBoxOpcode.MoveComponent,
|
||||
CircuitBoxSelectNodesEvent
|
||||
=> CircuitBoxOpcode.SelectComponents,
|
||||
CircuitBoxSelectWiresEvent
|
||||
=> CircuitBoxOpcode.SelectWires,
|
||||
CircuitBoxServerUpdateSelection
|
||||
=> CircuitBoxOpcode.UpdateSelection,
|
||||
(CircuitBoxClientAddWireEvent or CircuitBoxServerCreateWireEvent)
|
||||
=> CircuitBoxOpcode.AddWire,
|
||||
CircuitBoxRemoveWireEvent
|
||||
=> CircuitBoxOpcode.RemoveWire,
|
||||
CircuitBoxInitializeStateFromServerEvent
|
||||
=> CircuitBoxOpcode.ServerInitialize,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(Data))
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class CircuitBoxNode : CircuitBoxSelectable
|
||||
{
|
||||
public Vector2 Size;
|
||||
public RectangleF Rect;
|
||||
private Vector2 position;
|
||||
|
||||
public Vector2 Position
|
||||
{
|
||||
get => position;
|
||||
set
|
||||
{
|
||||
const float clampSize = CircuitBoxSizes.PlayableAreaSize / 2f;
|
||||
|
||||
position = new Vector2(Math.Clamp(value.X, -clampSize, clampSize),
|
||||
Math.Clamp(value.Y, -clampSize, clampSize));
|
||||
UpdatePositions();
|
||||
}
|
||||
}
|
||||
|
||||
public ImmutableArray<CircuitBoxConnection> Connectors;
|
||||
|
||||
public static float Opacity = 0.8f;
|
||||
|
||||
public readonly CircuitBox CircuitBox;
|
||||
|
||||
public CircuitBoxNode(CircuitBox circuitBox)
|
||||
{
|
||||
CircuitBox = circuitBox;
|
||||
}
|
||||
|
||||
public static Vector2 CalculateSize(IReadOnlyList<CircuitBoxConnection> conns)
|
||||
{
|
||||
Vector2 leftSize = Vector2.Zero,
|
||||
rightSize = Vector2.Zero;
|
||||
|
||||
foreach (var c in conns)
|
||||
{
|
||||
if (c.IsOutput)
|
||||
{
|
||||
rightSize.X = MathF.Max(rightSize.X, c.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
leftSize.X = MathF.Max(leftSize.X, c.Length);
|
||||
}
|
||||
|
||||
if (c.IsOutput)
|
||||
{
|
||||
rightSize.Y += CircuitBoxConnection.Size;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftSize.Y += CircuitBoxConnection.Size;
|
||||
}
|
||||
}
|
||||
|
||||
return new Vector2(leftSize.X + CircuitBoxSizes.NodeInitialPadding + rightSize.X, CircuitBoxSizes.NodeInitialPadding + MathF.Max(leftSize.Y, rightSize.Y));
|
||||
}
|
||||
|
||||
protected void UpdatePositions()
|
||||
{
|
||||
Vector2 rectStart = Position - Size / 2f;
|
||||
Vector2 rectSize = Size;
|
||||
rectSize.Y += CircuitBoxSizes.NodeHeaderHeight;
|
||||
Rect = new RectangleF(rectStart, rectSize);
|
||||
|
||||
#if CLIENT
|
||||
UpdateDrawRects();
|
||||
#endif
|
||||
|
||||
int leftIndex = 0,
|
||||
rightIndex = 0;
|
||||
|
||||
int inputCount = 0,
|
||||
outputCount = 0;
|
||||
|
||||
foreach (var c in Connectors)
|
||||
{
|
||||
if (c.IsOutput)
|
||||
{
|
||||
outputCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
inputCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 drawPos = Position;
|
||||
drawPos.Y = -drawPos.Y;
|
||||
|
||||
foreach (var c in Connectors)
|
||||
{
|
||||
bool isOutput = c.IsOutput;
|
||||
|
||||
int yIndex = isOutput ? rightIndex : leftIndex;
|
||||
int count = isOutput ? outputCount : inputCount;
|
||||
|
||||
float totalHeight = (count * CircuitBoxConnection.Size) / 2f;
|
||||
float y = (yIndex * CircuitBoxConnection.Size) - totalHeight;
|
||||
|
||||
float halfWidth = Rect.Width / 2f - CircuitBoxConnection.Size / 2f;
|
||||
|
||||
halfWidth -= 16f;
|
||||
|
||||
float xOffset = c.IsOutput ? halfWidth : -halfWidth;
|
||||
|
||||
Vector2 inputPos = drawPos + new Vector2(xOffset, y + c.Rect.Height / 2f);
|
||||
c.Position = inputPos;
|
||||
|
||||
if (isOutput)
|
||||
{
|
||||
rightIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal class CircuitBoxSelectable
|
||||
{
|
||||
public bool IsSelected;
|
||||
public ushort SelectedBy;
|
||||
|
||||
public bool IsSelectedByMe
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GameMain.NetworkMember is { IsServer: true })
|
||||
{
|
||||
throw new Exception("CircuitBoxSelectable.IsSelectedByMe should never be used by the server.");
|
||||
}
|
||||
|
||||
if (Character.Controlled is { } controlled)
|
||||
{
|
||||
return SelectedBy == controlled.ID;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSelected(Option<ushort> selectedBy)
|
||||
{
|
||||
if (selectedBy.TryUnwrap(out ushort id))
|
||||
{
|
||||
SelectedBy = id;
|
||||
IsSelected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
IsSelected = false;
|
||||
SelectedBy = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal static class CircuitBoxSizes
|
||||
{
|
||||
public const int ConnectorSize = 32;
|
||||
public const int AnchorOffset = 24;
|
||||
public const int NodeHeaderHeight = 48;
|
||||
public const int NodeInitialPadding = 64;
|
||||
public const int WireWidth = 10;
|
||||
public const int WireKnobLength = 16;
|
||||
public const int NodeHeaderTextPadding = 8;
|
||||
|
||||
public const float PlayableAreaSize = 8192f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class CircuitBoxWire : CircuitBoxSelectable, ICircuitBoxIdentifiable
|
||||
{
|
||||
public CircuitBoxConnection From, To;
|
||||
public readonly Option<Item> BackingWire;
|
||||
|
||||
public readonly Color Color;
|
||||
public readonly ItemPrefab UsedItemPrefab;
|
||||
|
||||
public ushort ID { get; }
|
||||
|
||||
public CircuitBoxWire(CircuitBox circuitBox, ushort Id, Option<Item> backingItem, CircuitBoxConnection from, CircuitBoxConnection to, ItemPrefab prefab)
|
||||
{
|
||||
ID = Id;
|
||||
From = from;
|
||||
To = to;
|
||||
BackingWire = backingItem;
|
||||
Color = prefab.SpriteColor;
|
||||
UsedItemPrefab = prefab;
|
||||
#if CLIENT
|
||||
Renderer = new CircuitBoxWireRenderer(Option.Some(this), to.AnchorPoint, from.AnchorPoint, Color, circuitBox.WireSprite);
|
||||
#endif
|
||||
EnsureWireConnected();
|
||||
}
|
||||
|
||||
public XElement Save()
|
||||
{
|
||||
XElement element = new XElement("Wire",
|
||||
new XAttribute("id", ID),
|
||||
new XAttribute("backingitemid", BackingWire.TryUnwrap(out var item) ? ItemSlotIndexPair.Serialize(item) : string.Empty),
|
||||
new XAttribute("prefab", UsedItemPrefab.Identifier));
|
||||
|
||||
XElement fromElement = CircuitBoxConnectorIdentifier.FromConnection(From).Save("From"),
|
||||
toElement = CircuitBoxConnectorIdentifier.FromConnection(To).Save("To");
|
||||
|
||||
element.Add(fromElement);
|
||||
element.Add(toElement);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
public static Option<CircuitBoxWire> TryLoadFromXML(ContentXElement element, CircuitBox circuitBox)
|
||||
{
|
||||
ushort id = element.GetAttributeUInt16("id", ICircuitBoxIdentifiable.NullComponentID);
|
||||
var backingItemIdOption = ItemSlotIndexPair.TryDeserializeFromXML(element, "backingitemid");
|
||||
Identifier usedPrefabIdentifier = element.GetAttributeIdentifier("prefab", Identifier.Empty);
|
||||
|
||||
if (!ItemPrefab.Prefabs.TryGet(usedPrefabIdentifier, out var itemPrefab))
|
||||
{
|
||||
DebugConsole.ThrowErrorAndLogToGA("CircuitBoxWire.TryLoadFromXML:PrefabNotFound",
|
||||
$"Failed to find prefab used to create wire with identifier {usedPrefabIdentifier} for CircuitBoxWire with ID {id}");
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
Option<Item> backingItem = Option.None;
|
||||
if (backingItemIdOption.TryUnwrap(out var backingItemIdPair))
|
||||
{
|
||||
if (backingItemIdPair.FindItemInContainer(circuitBox.WireContainer) is { } item)
|
||||
{
|
||||
backingItem = Option.Some(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowErrorAndLogToGA("CircuitBoxWire.TryLoadFromXML:IdNotFound",
|
||||
$"Failed to find item with ID {backingItemIdPair} for CircuitBoxWire with ID {id}");
|
||||
return Option.None;
|
||||
}
|
||||
}
|
||||
|
||||
Option<CircuitBoxConnection> From = Option.None,
|
||||
To = Option.None;
|
||||
|
||||
foreach (ContentXElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "from":
|
||||
var fromIdentifier = CircuitBoxConnectorIdentifier.Load(subElement);
|
||||
if (fromIdentifier.FindConnection(circuitBox).TryUnwrap(out var fromConnection))
|
||||
{
|
||||
From = Option.Some(fromConnection);
|
||||
}
|
||||
break;
|
||||
case "to":
|
||||
var toIdentifier = CircuitBoxConnectorIdentifier.Load(subElement);
|
||||
if (toIdentifier.FindConnection(circuitBox).TryUnwrap(out var toConnection))
|
||||
{
|
||||
To = Option.Some(toConnection);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (From.TryUnwrap(out var from) && To.TryUnwrap(out var to))
|
||||
{
|
||||
return Option.Some(new CircuitBoxWire(circuitBox, id, backingItem, from, to, itemPrefab));
|
||||
}
|
||||
|
||||
DebugConsole.ThrowErrorAndLogToGA("CircuitBoxWire.TryLoadFromXML:MissingFromOrTo",
|
||||
$"Failed to load CircuitBoxWire with ID {id}, missing \"From\" or \"To\" connection.");
|
||||
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
public void EnsureWireConnected()
|
||||
{
|
||||
EnsureExternalConnection(From, To);
|
||||
EnsureExternalConnection(To, From);
|
||||
|
||||
if (!BackingWire.TryUnwrap(out var item) || item.GetComponent<Wire>() is not { } wire) { return; }
|
||||
|
||||
wire.DropOnConnect = false;
|
||||
|
||||
From.Connection.ConnectWire(wire);
|
||||
To.Connection.ConnectWire(wire);
|
||||
|
||||
wire.Connect(From.Connection, 0, addNode: false, sendNetworkEvent: false);
|
||||
wire.Connect(To.Connection, 1, addNode: false, sendNetworkEvent: false);
|
||||
|
||||
static void EnsureExternalConnection(CircuitBoxConnection one, CircuitBoxConnection two)
|
||||
{
|
||||
switch (one)
|
||||
{
|
||||
case CircuitBoxInputConnection input:
|
||||
{
|
||||
if (input.ExternallyConnectedTo.Contains(two)) { break; }
|
||||
input.ExternallyConnectedTo.Add(two);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOutputConnection output:
|
||||
{
|
||||
if (output.ExternallyConnectedFrom.Contains(two)) { break; }
|
||||
output.ExternallyConnectedFrom.Add(two);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxNodeConnection node when two is CircuitBoxOutputConnection output:
|
||||
{
|
||||
if (node.Connection.CircuitBoxConnections.Contains(output)) { break; }
|
||||
node.Connection.CircuitBoxConnections.Add(output);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxNodeConnection node when two is CircuitBoxInputConnection input:
|
||||
{
|
||||
if (node.ExternallyConnectedFrom.Contains(input)) { break; }
|
||||
node.ExternallyConnectedFrom.Add(input);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
// client should not remove wires
|
||||
if (GameMain.NetworkMember is { IsClient: true }) { return; }
|
||||
|
||||
if (!BackingWire.TryUnwrap(out var wireItem)) { return; }
|
||||
|
||||
if (Entity.Spawner is { } spawner && Screen.Selected is not { IsEditor: true })
|
||||
{
|
||||
spawner.AddEntityToRemoveQueue(wireItem);
|
||||
return;
|
||||
}
|
||||
|
||||
Wire? wire = wireItem.GetComponent<Wire>();
|
||||
if (wire is not null)
|
||||
{
|
||||
From.Connection.DisconnectWire(wire);
|
||||
To.Connection.DisconnectWire(wire);
|
||||
}
|
||||
// if EntitySpawner is not available
|
||||
wireItem.Remove();
|
||||
}
|
||||
|
||||
public static ItemPrefab DefaultWirePrefab => ItemPrefab.Prefabs[Tags.RedWire];
|
||||
public static ItemPrefab SelectedWirePrefab = DefaultWirePrefab;
|
||||
public static readonly Color DefaultWireColor = DefaultWirePrefab.SpriteColor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public interface ICircuitBoxIdentifiable
|
||||
{
|
||||
public const ushort NullComponentID = ushort.MaxValue;
|
||||
|
||||
public ushort ID { get; }
|
||||
|
||||
public static ushort FindFreeID<T>(IReadOnlyCollection<T> ids) where T : ICircuitBoxIdentifiable
|
||||
{
|
||||
var sortedIds = ids.Select(static i => i.ID).ToImmutableHashSet();
|
||||
|
||||
for (ushort i = 0; i < NullComponentID - 1; i++)
|
||||
{
|
||||
if (!sortedIds.Contains(i))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return NullComponentID;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal readonly record struct ItemSlotIndexPair(int Slot, int StackIndex)
|
||||
{
|
||||
public static Option<ItemSlotIndexPair> TryDeserializeFromXML(ContentXElement element, string elementName)
|
||||
{
|
||||
string? elementStr = element.GetAttributeString(elementName, string.Empty);
|
||||
if (string.IsNullOrEmpty(elementStr)) { return Option.None; }
|
||||
|
||||
var point = XMLExtensions.ParsePoint(elementStr);
|
||||
return Option.Some(new ItemSlotIndexPair(point.X, point.Y));
|
||||
}
|
||||
|
||||
public static string Serialize(Item item)
|
||||
{
|
||||
Inventory parent = item.ParentInventory;
|
||||
if (item.ParentInventory is null)
|
||||
{
|
||||
throw new Exception($"Item \"{item.Name}\" is not in an inventory.");
|
||||
}
|
||||
|
||||
int slotIndex = parent.FindIndex(item);
|
||||
|
||||
int stackIndex = parent.GetItemStackSlotIndex(item, slotIndex);
|
||||
|
||||
if (slotIndex < 0 || stackIndex < 0)
|
||||
{
|
||||
throw new Exception($"Unable to find item \"{item.Name}\" in its parent inventory.");
|
||||
}
|
||||
|
||||
return XMLExtensions.PointToString(new Point(slotIndex, stackIndex));
|
||||
}
|
||||
|
||||
public Item? FindItemInContainer(ItemContainer? container)
|
||||
=> container?.Inventory.GetItemsAt(Slot).ElementAt(StackIndex);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user