Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/CircuitBox.cs
2024-06-18 16:50:02 +03:00

828 lines
31 KiB
C#

#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
namespace Barotrauma.Items.Components
{
internal sealed partial class CircuitBox : ItemComponent, IClientSerializable, IServerSerializable
{
public static readonly ImmutableHashSet<CircuitBoxOpcode> UnrealiableOpcodes
= ImmutableHashSet.Create(CircuitBoxOpcode.Cursor);
public ImmutableArray<CircuitBoxInputConnection> Inputs;
public ImmutableArray<CircuitBoxOutputConnection> Outputs;
public readonly List<CircuitBoxComponent> Components = new List<CircuitBoxComponent>();
public readonly List<CircuitBoxInputOutputNode> InputOutputNodes = new();
public readonly List<CircuitBoxLabelNode> Labels = new();
public readonly List<CircuitBoxWire> Wires = new List<CircuitBoxWire>();
public override bool IsActive => true;
// We don't want the components and wires to transfer between subs as it would cause issues.
public override bool DontTransferInventoryBetweenSubs => true;
// We don't want to sell the components and wires inside the circuit box
public override bool DisallowSellingItemsFromContainer => true;
public Option<CircuitBoxConnection> FindInputOutputConnection(Identifier connectionName)
{
foreach (CircuitBoxInputConnection input in Inputs)
{
if (input.Name != connectionName) { continue; }
return Option.Some<CircuitBoxConnection>(input);
}
foreach (CircuitBoxOutputConnection output in Outputs)
{
if (output.Name != connectionName) { continue; }
return Option.Some<CircuitBoxConnection>(output);
}
return Option.None;
}
public Option<CircuitBoxConnection> FindInputOutputConnection(Connection connection)
{
foreach (CircuitBoxInputConnection input in Inputs)
{
if (input.Connection != connection) { continue; }
return Option.Some<CircuitBoxConnection>(input);
}
foreach (CircuitBoxOutputConnection output in Outputs)
{
if (output.Connection != connection) { continue; }
return Option.Some<CircuitBoxConnection>(output);
}
return Option.None;
}
public readonly ItemContainer[] containers;
private const int ComponentContainerIndex = 0,
WireContainerIndex = 1;
public ItemContainer? ComponentContainer
=> GetContainerOrNull(ComponentContainerIndex);
// wire container falls back to the main container if one isn't specified
public ItemContainer? WireContainer
=> GetContainerOrNull(WireContainerIndex) ?? GetContainerOrNull(ComponentContainerIndex);
public bool IsFull => ComponentContainer?.Inventory is { } inventory && inventory.IsFull(true);
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Locked circuit boxes can only be viewed and not interacted with.")]
public bool Locked { get; set; }
public CircuitBox(Item item, ContentXElement element) : base(item, element)
{
containers = item.GetComponents<ItemContainer>().ToArray();
if (containers.Length < 1)
{
DebugConsole.ThrowError("Circuit box must have at least one item container to function.");
}
InitProjSpecific(element);
var inputBuilder = ImmutableArray.CreateBuilder<CircuitBoxInputConnection>();
var outputBuilder = ImmutableArray.CreateBuilder<CircuitBoxOutputConnection>();
foreach (Connection conn in Item.Connections)
{
if (conn.IsOutput)
{
outputBuilder.Add(new CircuitBoxOutputConnection(Vector2.Zero, conn, this));
}
else
{
inputBuilder.Add(new CircuitBoxInputConnection(Vector2.Zero, conn, this));
}
}
Inputs = inputBuilder.ToImmutable();
Outputs = outputBuilder.ToImmutable();
InputOutputNodes.Add(new CircuitBoxInputOutputNode(Inputs, new Vector2(-512, 0f), CircuitBoxInputOutputNode.Type.Input, this));
InputOutputNodes.Add(new CircuitBoxInputOutputNode(Outputs, new Vector2(512, 0f), CircuitBoxInputOutputNode.Type.Output, this));
item.OnDeselect += OnDeselected;
}
/// <summary>
/// We want to load the components after the map has loaded since we need to link up the components to their items
/// and pretty much all items have higher ID than the circuit box.
/// </summary>
private Option<ContentXElement> delayedElementToLoad;
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap, bool isItemSwap)
{
base.Load(componentElement, usePrefabValues, idRemap, isItemSwap);
if (delayedElementToLoad.IsSome()) { return; }
delayedElementToLoad = Option.Some(componentElement);
}
public override void OnInventoryChanged()
=> OnViewUpdateProjSpecific();
public override void Update(float deltaTime, Camera cam)
{
#if CLIENT
// When loading from the server the wires cannot be properly loaded and connected up because we might not be loaded in properly yet.
// So we need to wait until the circuit box starts updating and then we can ensure the wires are connected.
if (wasInitializedByServer)
{
foreach (var w in Wires)
{
w.EnsureWireConnected();
}
wasInitializedByServer = false;
}
#endif
TryInitializeNodes();
}
public override void OnMapLoaded()
=> TryInitializeNodes();
private void TryInitializeNodes()
{
if (!delayedElementToLoad.TryUnwrap(out var loadElement)) { return; }
LoadFromXML(loadElement);
delayedElementToLoad = Option.None;
}
public void LoadFromXML(ContentXElement loadElement)
{
foreach (var subElement in loadElement.Elements())
{
string elementName = subElement.Name.ToString().ToLowerInvariant();
switch (elementName)
{
case "component" when CircuitBoxComponent.TryLoadFromXML(subElement, this).TryUnwrap(out var comp):
Components.Add(comp);
break;
case "wire" when CircuitBoxWire.TryLoadFromXML(subElement, this).TryUnwrap(out var wire):
Wires.Add(wire);
break;
case "inputnode":
LoadFor(CircuitBoxInputOutputNode.Type.Input, subElement);
break;
case "outputnode":
LoadFor(CircuitBoxInputOutputNode.Type.Output, subElement);
break;
case "label":
Labels.Add(CircuitBoxLabelNode.LoadFromXML(subElement, this));
break;
}
}
#if SERVER
// We need to let the clients know of the loaded data
if (needsServerInitialization)
{
CreateInitializationEvent();
needsServerInitialization = false;
}
#endif
void LoadFor(CircuitBoxInputOutputNode.Type type, ContentXElement subElement)
{
foreach (var node in InputOutputNodes)
{
if (node.NodeType != type) { continue; }
node.Load(subElement);
break;
}
}
}
public void CloneFrom(CircuitBox original, Dictionary<ushort, Item> clonedContainedItems)
{
Components.Clear();
Wires.Clear();
Labels.Clear();
foreach (var label in original.Labels)
{
var newLabel = new CircuitBoxLabelNode(label.ID, label.Color, label.Position, this);
newLabel.EditText(label.HeaderText, label.BodyText);
newLabel.ApplyResize(label.Size, label.Position);
Labels.Add(newLabel);
}
for (int ioIndex = 0; ioIndex < original.InputOutputNodes.Count; ioIndex++)
{
var origNode = original.InputOutputNodes[ioIndex];
var cloneNode = InputOutputNodes[ioIndex];
cloneNode.Position = origNode.Position;
}
if (!clonedContainedItems.Any()) { return; }
foreach (var origComp in original.Components)
{
if (!clonedContainedItems.TryGetValue(origComp.Item.ID, out var clonedItem)) { continue; }
var newComponent = new CircuitBoxComponent(origComp.ID, clonedItem, origComp.Position, this, origComp.UsedResource);
Components.Add(newComponent);
}
foreach (var origWire in original.Wires)
{
Option<CircuitBoxConnection> to = CircuitBoxConnectorIdentifier.FromConnection(origWire.To).FindConnection(this),
from = CircuitBoxConnectorIdentifier.FromConnection(origWire.From).FindConnection(this);
if (!to.TryUnwrap(out var toConn) || !from.TryUnwrap(out var fromConn))
{
DebugConsole.ThrowError($"Error while cloning item \"{Name}\" - failed to find a connection for a wire. ");
continue;
}
var wireItem = origWire.BackingWire.Select(w => clonedContainedItems[w.ID]);
var newWire = new CircuitBoxWire(this, origWire.ID, wireItem, fromConn, toConn, origWire.UsedItemPrefab);
Wires.Add(newWire);
}
}
public override XElement Save(XElement parentElement)
{
XElement componentElement = base.Save(parentElement);
foreach (CircuitBoxInputOutputNode node in InputOutputNodes)
{
componentElement.Add(node.Save());
}
foreach (CircuitBoxComponent node in Components)
{
componentElement.Add(node.Save());
}
foreach (CircuitBoxWire wire in Wires)
{
componentElement.Add(wire.Save());
}
foreach (var label in Labels)
{
componentElement.Add(label.Save());
}
return componentElement;
}
public partial void OnDeselected(Character c);
public record struct CreatedWire(CircuitBoxConnectorIdentifier Start, CircuitBoxConnectorIdentifier End, Option<Item> Item, ushort ID);
public bool Connect(CircuitBoxConnection one, CircuitBoxConnection two, Action<CreatedWire> onCreated, ItemPrefab selectedWirePrefab)
{
if (!VerifyConnection(one, two)) { return false; }
ushort id = ICircuitBoxIdentifiable.FindFreeID(Wires);
switch (one.IsOutput)
{
case true when !two.IsOutput:
{
CircuitBoxConnectorIdentifier start = CircuitBoxConnectorIdentifier.FromConnection(one),
end = CircuitBoxConnectorIdentifier.FromConnection(two);
if (IsExternalConnection(one) || IsExternalConnection(two))
{
CreateWireWithoutItem(one, two, id, selectedWirePrefab);
onCreated(new CreatedWire(start, end, Option.None, id));
return true;
}
CreateWireWithItem(one, two, selectedWirePrefab, id, i => onCreated(new CreatedWire(start, end, Option.Some(i), id)));
return true;
}
case false when two.IsOutput:
{
CircuitBoxConnectorIdentifier start = CircuitBoxConnectorIdentifier.FromConnection(two),
end = CircuitBoxConnectorIdentifier.FromConnection(one);
if (IsExternalConnection(one) || IsExternalConnection(two))
{
CreateWireWithoutItem(two, one, id, selectedWirePrefab);
onCreated(new CreatedWire(start, end, Option.None, id));
return true;
}
CreateWireWithItem(two, one, selectedWirePrefab, id, i => onCreated(new CreatedWire(start, end, Option.Some(i), id)));
return true;
}
}
return false;
}
private static bool VerifyConnection(CircuitBoxConnection one, CircuitBoxConnection two)
{
if (one.IsOutput == two.IsOutput || one == two) { return false; }
if (one is CircuitBoxNodeConnection oneNodeConnection &&
two is CircuitBoxNodeConnection twoNodeConnection)
{
if (oneNodeConnection.Component == twoNodeConnection.Component)
{
return false;
}
}
if (one is CircuitBoxNodeConnection { HasAvailableSlots: false } ||
two is CircuitBoxNodeConnection { HasAvailableSlots: false })
{
return one is not CircuitBoxNodeConnection || two is not CircuitBoxNodeConnection;
}
return true;
}
private void AddLabelInternal(ushort id, Color color, Vector2 pos, NetLimitedString header, NetLimitedString body)
{
var newLabel = new CircuitBoxLabelNode(id, color, pos, this);
newLabel.EditText(header, body);
Labels.Add(newLabel);
OnViewUpdateProjSpecific();
}
private void RemoveLabelInternal(IReadOnlyCollection<ushort> ids)
{
foreach (CircuitBoxLabelNode node in Labels.ToImmutableArray())
{
if (!ids.Contains(node.ID)) { continue; }
Labels.Remove(node);
}
OnViewUpdateProjSpecific();
}
private void ResizeLabelInternal(ushort id, Vector2 pos, Vector2 size)
{
size = Vector2.Max(size, CircuitBoxLabelNode.MinSize);
foreach (CircuitBoxLabelNode node in Labels)
{
if (node.ID != id) { continue; }
node.ApplyResize(size, pos);
break;
}
OnViewUpdateProjSpecific();
}
private void RenameConnectionLabelsInternal(CircuitBoxInputOutputNode.Type type, Dictionary<string, string> overrides)
{
foreach (var node in InputOutputNodes)
{
if (node.NodeType != type) { continue; }
node.ReplaceAllConnectionLabelOverrides(overrides);
break;
}
OnViewUpdateProjSpecific();
}
private static bool IsExternalConnection(CircuitBoxConnection conn) => conn is (CircuitBoxInputConnection or CircuitBoxOutputConnection);
private void CreateWireWithoutItem(CircuitBoxConnection one, CircuitBoxConnection two, ushort id, ItemPrefab prefab)
{
bool hasExternalConnection = false;
if (one is CircuitBoxInputConnection input)
{
hasExternalConnection = true;
input.ExternallyConnectedTo.Add(two);
}
if (two is CircuitBoxOutputConnection output)
{
hasExternalConnection = true;
one.Connection.CircuitBoxConnections.Add(output);
}
if (hasExternalConnection)
{
two.ExternallyConnectedFrom.Add(one);
}
AddWireDirect(id, prefab, Option.None, one, two);
}
private void CreateWireWithItem(CircuitBoxConnection one, CircuitBoxConnection two, ItemPrefab prefab, ushort wireId, Action<Item> onItemSpawned)
{
if (WireContainer is null) { return; }
if (IsExternalConnection(one) || IsExternalConnection(two))
{
DebugConsole.ThrowError("Cannot add a wire between an external connection and a component connection.");
return;
}
SpawnItem(prefab, user: null, container: WireContainer, onSpawned: wire =>
{
AddWireDirect(wireId, prefab, Option.Some(wire), one, two);
onItemSpawned(wire);
});
}
private void CreateWireWithItem(CircuitBoxConnection one, CircuitBoxConnection two, ushort wireId, Item it)
{
if (IsExternalConnection(one) || IsExternalConnection(two))
{
DebugConsole.ThrowError("Cannot add a wire between an external connection and a component connection.");
return;
}
AddWireDirect(wireId, it.Prefab, Option.Some(it), one, two);
}
private void AddWireDirect(ushort id, ItemPrefab prefab, Option<Item> backingItem, CircuitBoxConnection one, CircuitBoxConnection two)
=> Wires.Add(new CircuitBoxWire(this, id, backingItem, one, two, prefab));
private void RenameLabelInternal(ushort id, Color color, NetLimitedString header, NetLimitedString body)
{
foreach (CircuitBoxLabelNode node in Labels)
{
if (node.ID != id) { continue; }
node.EditText(header, body);
node.Color = color;
break;
}
}
private bool AddComponentInternal(ushort id, ItemPrefab prefab, ItemPrefab usedResource, Vector2 pos, Character? user, Action<Item>? onItemSpawned)
{
if (id is ICircuitBoxIdentifiable.NullComponentID)
{
DebugConsole.ThrowError("Unable to add component because there are no free IDs.");
return false;
}
if (ComponentContainer?.Inventory is { } inventory && inventory.HowManyCanBePut(prefab) <= 0)
{
DebugConsole.ThrowError("Unable to add component because there is no space in the inventory.");
return false;
}
SpawnItem(prefab, user, ComponentContainer, spawnedItem =>
{
Components.Add(new CircuitBoxComponent(id, spawnedItem, pos, this, usedResource));
onItemSpawned?.Invoke(spawnedItem);
OnViewUpdateProjSpecific();
});
OnViewUpdateProjSpecific();
return true;
}
// Unsafe because it doesn't perform error checking since it's data we get from the server
private void AddComponentInternalUnsafe(ushort id, Item backingItem, ItemPrefab usedResource, Vector2 pos)
{
Components.Add(new CircuitBoxComponent(id, backingItem, pos, this, usedResource));
OnViewUpdateProjSpecific();
}
private static void ClearSelectionFor(ushort characterId, IReadOnlyCollection<CircuitBoxSelectable> nodes)
{
foreach (var node in nodes)
{
if (node.SelectedBy != characterId) { continue; }
node.SetSelected(Option.None);
}
}
private void ClearAllSelectionsInternal(ushort characterId)
{
ClearSelectionFor(characterId, Components);
ClearSelectionFor(characterId, InputOutputNodes);
ClearSelectionFor(characterId, Wires);
ClearSelectionFor(characterId, Labels);
}
private void SelectLabelsInternal(IReadOnlyCollection<ushort> ids, ushort characterId, bool overwrite)
{
if (overwrite) { ClearSelectionFor(characterId, Labels); }
if (!ids.Any()) { return; }
foreach (CircuitBoxLabelNode node in Labels)
{
if (!ids.Contains(node.ID)) { continue; }
node.SetSelected(Option.Some(characterId));
}
}
private void SelectComponentsInternal(IReadOnlyCollection<ushort> ids, ushort characterId, bool overwrite)
{
if (overwrite) { ClearSelectionFor(characterId, Components); }
if (!ids.Any()) { return; }
foreach (CircuitBoxComponent node in Components)
{
if (!ids.Contains(node.ID)) { continue; }
node.SetSelected(Option.Some(characterId));
}
}
private void UpdateSelections(ImmutableDictionary<ushort, Option<ushort>> nodeIds,
ImmutableDictionary<ushort, Option<ushort>> wireIds,
ImmutableDictionary<CircuitBoxInputOutputNode.Type, Option<ushort>> inputOutputs,
ImmutableDictionary<ushort, Option<ushort>> labels)
{
foreach (var wire in Wires)
{
if (!wireIds.TryGetValue(wire.ID, out var selectedBy)) { continue; }
if (selectedBy.TryUnwrap(out var id))
{
wire.IsSelected = true;
wire.SelectedBy = id;
continue;
}
wire.IsSelected = false;
wire.SelectedBy = 0;
}
foreach (var node in Components)
{
if (!nodeIds.TryGetValue(node.ID, out var selectedBy)) { continue; }
node.SetSelected(selectedBy);
}
foreach (var node in InputOutputNodes)
{
if (!inputOutputs.TryGetValue(node.NodeType, out var selectedBy)) { continue; }
node.SetSelected(selectedBy);
}
foreach (var node in Labels)
{
if (!labels.TryGetValue(node.ID, out var selectedBy)) { continue; }
node.SetSelected(selectedBy);
}
}
private void SelectWiresInternal(IReadOnlyCollection<ushort> ids, ushort characterId, bool overwrite)
{
if (overwrite) { ClearSelectionFor(characterId, Wires); }
foreach (CircuitBoxWire wire in Wires)
{
if (!ids.Contains(wire.ID)) { continue; }
wire.SetSelected(Option.Some(characterId));
}
}
private void SelectInputOutputInternal(IReadOnlyCollection<CircuitBoxInputOutputNode.Type> io, ushort characterId, bool overwrite)
{
if (overwrite) { ClearSelectionFor(characterId, InputOutputNodes); }
foreach (var node in InputOutputNodes)
{
if (!io.Contains(node.NodeType)) { continue; }
node.SetSelected(Option.Some(characterId));
}
}
private void RemoveComponentInternal(IReadOnlyCollection<ushort> ids)
{
foreach (CircuitBoxComponent node in Components.ToImmutableArray())
{
if (!ids.Contains(node.ID)) { continue; }
Components.Remove(node);
node.Remove();
foreach (CircuitBoxWire wire in Wires.ToImmutableArray())
{
if (node.Connectors.Contains(wire.From) || node.Connectors.Contains(wire.To))
{
RemoveWireCollectionUnsafe(wire);
}
}
}
OnViewUpdateProjSpecific();
}
private void RemoveWireInternal(IReadOnlyCollection<ushort> ids)
{
foreach (CircuitBoxWire wire in Wires.ToImmutableArray())
{
if (!ids.Contains(wire.ID)) { continue; }
RemoveWireCollectionUnsafe(wire);
}
OnViewUpdateProjSpecific();
}
private void RemoveWireCollectionUnsafe(CircuitBoxWire wire)
{
foreach (CircuitBoxOutputConnection output in Outputs)
{
output.Connection.CircuitBoxConnections.Remove(wire.From);
}
wire.From.Connection.CircuitBoxConnections.Remove(wire.To);
if (wire.From is CircuitBoxInputConnection input)
{
input.ExternallyConnectedTo.Remove(wire.To);
}
wire.To.ExternallyConnectedFrom.Remove(wire.From);
wire.From.ExternallyConnectedFrom.Remove(wire.To);
wire.Remove();
Wires.Remove(wire);
}
private void MoveNodesInternal(IReadOnlyCollection<ushort> ids,
IReadOnlyCollection<CircuitBoxInputOutputNode.Type> ios,
IReadOnlyCollection<ushort> labels,
Vector2 moveAmount)
{
IEnumerable<CircuitBoxComponent> nodes = Components.Where(node => ids.Contains(node.ID));
foreach (CircuitBoxComponent node in nodes)
{
node.Position += moveAmount;
}
foreach (var label in Labels.Where(n => labels.Contains(n.ID)))
{
label.Position += moveAmount;
}
foreach (var io in InputOutputNodes)
{
if (!ios.Contains(io.NodeType)) { continue; }
io.Position += moveAmount;
}
OnViewUpdateProjSpecific();
}
public override bool Select(Character character)
=> item.GetComponent<Holdable>() is not { Attached: false } && base.Select(character);
public partial void OnViewUpdateProjSpecific();
partial void InitProjSpecific(ContentXElement element);
public override void ReceiveSignal(Signal signal, Connection connection)
{
foreach (var input in Inputs)
{
if (input.Connection != connection) { continue; }
input.ReceiveSignal(signal);
break;
}
}
public static bool IsRoundRunning()
=> !Submarine.Unloading && GameMain.GameSession is { IsRunning: true };
public static Option<CircuitBox> FindCircuitBox(ushort itemId, byte componentIndex)
{
if (!IsRoundRunning() || Entity.FindEntityByID(itemId) is not Item item) { return Option.None; }
if (componentIndex >= item.Components.Count)
{
return Option.None;
}
ItemComponent targetComponent = item.Components[componentIndex];
if (targetComponent is CircuitBox circuitBox)
{
return Option.Some(circuitBox);
}
return Option.None;
}
private ItemContainer? GetContainerOrNull(int index) => index >= 0 && index < containers.Length ? containers[index] : null;
public void CreateRefundItemsForUsedResources(IReadOnlyCollection<ushort> ids, Character? character)
{
if (!IsInGame()) { return; }
var prefabsToCreate = Components.Where(comp => ids.Contains(comp.ID))
.Select(static comp => comp.UsedResource)
.ToImmutableArray();
foreach (ItemPrefab prefab in prefabsToCreate)
{
if (character?.Inventory is null)
{
Entity.Spawner?.AddItemToSpawnQueue(prefab, item.Position, item.Submarine);
}
else
{
Entity.Spawner?.AddItemToSpawnQueue(prefab, character.Inventory);
}
}
}
public static ImmutableArray<Item> GetSortedCircuitBoxItemsFromPlayer(Character? character)
=> character?.Inventory?.FindAllItems(predicate: CanItemBeAccessed, recursive: true)
.OrderBy(static i => i.Prefab.Identifier == Tags.FPGACircuit)
.ToImmutableArray() ?? ImmutableArray<Item>.Empty;
public static bool CanItemBeAccessed(Item item) =>
item.ParentInventory switch
{
ItemInventory ii => ii.Container.DrawInventory,
_ => true
};
public static Option<Item> GetApplicableResourcePlayerHas(ItemPrefab prefab, Character? character)
{
if (character is null) { return Option.None; }
return GetApplicableResourcePlayerHas(prefab, GetSortedCircuitBoxItemsFromPlayer(character));
}
public static Option<Item> GetApplicableResourcePlayerHas(ItemPrefab prefab, ImmutableArray<Item> playerItems)
{
foreach (var invItem in playerItems)
{
if (invItem.Prefab == prefab || invItem.Prefab.Identifier == Tags.FPGACircuit)
{
return Option.Some(invItem);
}
}
return Option.None;
}
public static void SpawnItem(ItemPrefab prefab, Character? user, ItemContainer? container, Action<Item> onSpawned)
{
if (container is null)
{
throw new Exception("Circuit box has no inventory");
}
if (IsInGame())
{
Entity.Spawner?.AddItemToSpawnQueue(prefab, container.Inventory, onSpawned: it =>
{
AssignWifiComponentTeam(it, user);
onSpawned(it);
});
return;
}
Item forceSpawnedItem = new Item(prefab, Vector2.Zero, null);
container.Inventory.TryPutItem(forceSpawnedItem, null);
onSpawned(forceSpawnedItem);
AssignWifiComponentTeam(forceSpawnedItem, user);
static void AssignWifiComponentTeam(Item item, Character? user)
{
if (user == null) { return; }
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
{
wifiComponent.TeamID = user.TeamID;
}
}
}
public static void RemoveItem(Item item)
{
if (IsInGame())
{
Entity.Spawner?.AddItemToRemoveQueue(item);
return;
}
item.Remove();
}
public static bool IsInGame()
=> Screen.Selected is not { IsEditor: true };
public static bool IsCircuitBoxSelected(Character character)
=> character.SelectedItem?.GetComponent<CircuitBox>() is not null;
}
}