#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 BackingWire; public readonly Color Color; public readonly ItemPrefab UsedItemPrefab; public ushort ID { get; } public CircuitBoxWire(CircuitBox circuitBox, ushort Id, Option 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 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 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 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() 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.Connection.CircuitBoxConnections.Contains(input)) { node.Connection.CircuitBoxConnections.Add(input); } if (!node.ExternallyConnectedFrom.Contains(input)) { 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(); 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; } }