Unstable 1.1.14.0
This commit is contained in:
+1
-1
@@ -57,7 +57,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.Yes, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("0", IsPropertySaveable.Yes, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
|
||||
@@ -0,0 +1,684 @@
|
||||
#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<CircuitBoxWire> Wires = new List<CircuitBoxWire>();
|
||||
|
||||
public override bool IsActive => 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 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);
|
||||
|
||||
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)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
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;
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
|
||||
#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();
|
||||
|
||||
foreach (var origComp in original.Components)
|
||||
{
|
||||
var newComponent = new CircuitBoxComponent(origComp.ID, clonedContainedItems[origComp.Item.ID], origComp.Position, this, origComp.UsedResource);
|
||||
Components.Add(newComponent);
|
||||
}
|
||||
|
||||
for (int ioIndex = 0; ioIndex < original.InputOutputNodes.Count; ioIndex++)
|
||||
{
|
||||
var origNode = original.InputOutputNodes[ioIndex];
|
||||
var cloneNode = InputOutputNodes[ioIndex];
|
||||
|
||||
cloneNode.Position = origNode.Position;
|
||||
}
|
||||
|
||||
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 newWire = new CircuitBoxWire(this, origWire.ID, origWire.BackingWire.Select(w => clonedContainedItems[w.ID]), 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());
|
||||
}
|
||||
|
||||
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 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(this, prefab, WireContainer, 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 bool AddComponentInternal(ushort id, ItemPrefab prefab, ItemPrefab usedResource, Vector2 pos, 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(this, prefab, ComponentContainer, spawnedItem =>
|
||||
{
|
||||
Components.Add(new CircuitBoxComponent(id, spawnedItem, pos, this, usedResource));
|
||||
onItemSpawned(spawnedItem);
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
Vector2 moveAmount)
|
||||
{
|
||||
IEnumerable<CircuitBoxComponent> nodes = Components.Where(node => ids.Contains(node.ID));
|
||||
foreach (CircuitBoxComponent node in nodes)
|
||||
{
|
||||
node.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> GetSortedCircuitBoxSortedItemsFromPlayer(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, GetSortedCircuitBoxSortedItemsFromPlayer(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(CircuitBox circuitBox, ItemPrefab prefab, 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: onSpawned);
|
||||
return;
|
||||
}
|
||||
|
||||
Item forceSpawnedItem = new Item(prefab, Vector2.Zero, null);
|
||||
container.Inventory.TryPutItem(forceSpawnedItem, null);
|
||||
onSpawned(forceSpawnedItem);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,15 @@ namespace Barotrauma.Items.Components
|
||||
private readonly HashSet<Wire> wires;
|
||||
public IReadOnlyCollection<Wire> Wires => wires;
|
||||
|
||||
/// <summary>
|
||||
/// Circuit box input and output connections that are linked to this connection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We don't want to create a wire between the circuit boxes connection panel and the
|
||||
/// connection panel of the item inside the circuit box so we use this to bridge the gap.
|
||||
/// </remarks>
|
||||
public List<CircuitBoxConnection> CircuitBoxConnections = new();
|
||||
|
||||
private bool enumeratingWires;
|
||||
private readonly HashSet<Wire> removedWires = new HashSet<Wire>();
|
||||
|
||||
@@ -177,6 +186,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the the connection is connected to a wire or a circuit box connection
|
||||
/// </summary>
|
||||
public bool IsConnectedToSomething()
|
||||
=> wires.Count > 0 || CircuitBoxConnections.Count > 0;
|
||||
|
||||
public void SetRecipientsDirty()
|
||||
{
|
||||
recipientsDirty = true;
|
||||
@@ -304,25 +319,15 @@ namespace Barotrauma.Items.Components
|
||||
if (recipient.item == this.item || signal.source?.LastSentSignalRecipients.LastOrDefault() == recipient) { continue; }
|
||||
|
||||
signal.source?.LastSentSignalRecipients.Add(recipient);
|
||||
|
||||
Connection connection = recipient;
|
||||
connection.LastReceivedSignal = signal;
|
||||
#if CLIENT
|
||||
wire.RegisterSignal(signal, source: this);
|
||||
#endif
|
||||
SendSignalIntoConnection(signal, recipient);
|
||||
}
|
||||
|
||||
foreach (ItemComponent ic in recipient.item.Components)
|
||||
{
|
||||
ic.ReceiveSignal(signal, connection);
|
||||
}
|
||||
|
||||
if (recipient.Effects != null && signal.value != "0")
|
||||
{
|
||||
foreach (StatusEffect effect in recipient.Effects)
|
||||
{
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step);
|
||||
}
|
||||
}
|
||||
foreach (CircuitBoxConnection connection in CircuitBoxConnections)
|
||||
{
|
||||
connection.ReceiveSignal(signal);
|
||||
}
|
||||
enumeratingWires = false;
|
||||
foreach (var removedWire in removedWires)
|
||||
@@ -331,7 +336,24 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
removedWires.Clear();
|
||||
}
|
||||
|
||||
|
||||
public static void SendSignalIntoConnection(Signal signal, Connection conn)
|
||||
{
|
||||
conn.LastReceivedSignal = signal;
|
||||
|
||||
foreach (ItemComponent ic in conn.item.Components)
|
||||
{
|
||||
ic.ReceiveSignal(signal, conn);
|
||||
}
|
||||
|
||||
if (conn.Effects == null || signal.value == "0") { return; }
|
||||
|
||||
foreach (StatusEffect effect in conn.Effects)
|
||||
{
|
||||
conn.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearConnections()
|
||||
{
|
||||
if (IsPower && Grid != null)
|
||||
|
||||
+13
-5
@@ -9,7 +9,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class ConnectionPanel : ItemComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
public List<Connection> Connections;
|
||||
const int MaxConnectionCount = 256;
|
||||
public readonly List<Connection> Connections = new List<Connection>();
|
||||
|
||||
private Character user;
|
||||
|
||||
@@ -67,10 +68,13 @@ namespace Barotrauma.Items.Components
|
||||
public ConnectionPanel(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
Connections = new List<Connection>();
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (Connections.Count == MaxConnectionCount)
|
||||
{
|
||||
DebugConsole.ThrowError($"Too many connections in the item {item.Prefab.Identifier} (> {MaxConnectionCount}).");
|
||||
break;
|
||||
}
|
||||
switch (subElement.Name.ToString())
|
||||
{
|
||||
case "input":
|
||||
@@ -179,7 +183,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
UpdateProjSpecific(deltaTime);
|
||||
|
||||
if (user == null || user.SelectedItem != item)
|
||||
if (user == null || (user.SelectedItem != item && user.SelectedSecondaryItem != item))
|
||||
{
|
||||
#if SERVER
|
||||
if (user != null) { item.CreateServerEvent(this); }
|
||||
@@ -208,6 +212,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public bool CanRewire()
|
||||
{
|
||||
if (item.Container?.GetComponent<CircuitBox>() != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//attaching wires to items with a body is not allowed
|
||||
//(signal items remove their bodies when attached to a wall)
|
||||
if (item.body != null && item.body.BodyType == FarseerPhysics.BodyType.Dynamic)
|
||||
@@ -395,7 +403,7 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
TriggerRewiringSound();
|
||||
#endif
|
||||
|
||||
msg.WriteByte((byte)Connections.Count);
|
||||
foreach (Connection connection in Connections)
|
||||
{
|
||||
msg.WriteVariableUInt32((uint)connection.Wires.Count);
|
||||
|
||||
@@ -299,9 +299,12 @@ namespace Barotrauma.Items.Components
|
||||
//make sure the clients know about the states of the checkboxes and text fields
|
||||
if (customInterfaceElementList.Any())
|
||||
{
|
||||
if (item.Submarine == null || !item.Submarine.Loading)
|
||||
if (item.FullyInitialized)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
if (!item.Removed) { item.CreateServerEvent(this); }
|
||||
}, delay: 0.1f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.Yes, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("0", IsPropertySaveable.Yes, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
|
||||
@@ -198,6 +198,22 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the red component of the light is twice as bright as the blue and green. Can be used by StatusEffects.
|
||||
/// </summary>
|
||||
public bool IsRed => ColorExtensions.IsRedDominant(LightColor);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the green component of the light is twice as bright as the red and blue. Can be used by StatusEffects.
|
||||
/// </summary>
|
||||
public bool IsGreen => ColorExtensions.IsGreenDominant(LightColor);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the blue component of the light is twice as bright as the red and green. Can be used by StatusEffects.
|
||||
/// </summary>
|
||||
public bool IsBlue => ColorExtensions.IsBlueDominant(LightColor);
|
||||
|
||||
|
||||
public float TemporaryFlickerTimer;
|
||||
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string falseOutput;
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.Yes, description: "The signal the item outputs when it has not detected movement.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("0", IsPropertySaveable.Yes, description: "The signal the item outputs when it has not detected movement.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
@@ -136,7 +136,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(DecimalCount = 3), Serialize(0.1f, IsPropertySaveable.Yes, description: "How fast the objects within the detector's range have to be moving (in m/s).", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable(DecimalCount = 3), Serialize(0.0f, IsPropertySaveable.Yes, description: "How fast the objects within the detector's range have to be moving (in m/s).", alwaysUseInstanceValues: true)]
|
||||
public float MinimumVelocity
|
||||
{
|
||||
get;
|
||||
@@ -254,45 +254,52 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (Target.HasFlag(TargetType.Human) || Target.HasFlag(TargetType.Pet) || Target.HasFlag(TargetType.Monster))
|
||||
bool triggerFromHumans = Target.HasFlag(TargetType.Human);
|
||||
bool triggerFromPets = Target.HasFlag(TargetType.Pet);
|
||||
bool triggerFromMonsters = Target.HasFlag(TargetType.Monster);
|
||||
bool hasTriggers = triggerFromHumans || triggerFromPets || triggerFromMonsters;
|
||||
if (!hasTriggers) { return; }
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
foreach (Character c in Character.CharacterList)
|
||||
if (IgnoreDead && c.IsDead) { continue; }
|
||||
|
||||
//ignore characters that have spawned a second or less ago
|
||||
//makes it possible to detect when a spawned character moves without triggering the detector immediately as the ragdoll spawns and drops to the ground
|
||||
if (c.SpawnTime > Timing.TotalTime - 1.0) { continue; }
|
||||
if (c.IsHuman)
|
||||
{
|
||||
if (IgnoreDead && c.IsDead) { continue; }
|
||||
|
||||
//ignore characters that have spawned a second or less ago
|
||||
//makes it possible to detect when a spawned character moves without triggering the detector immediately as the ragdoll spawns and drops to the ground
|
||||
if (c.SpawnTime > Timing.TotalTime - 1.0) { continue; }
|
||||
|
||||
if (c.IsHuman)
|
||||
{
|
||||
if (!Target.HasFlag(TargetType.Human)) { continue; }
|
||||
}
|
||||
else if (c.IsPet)
|
||||
{
|
||||
if (!Target.HasFlag(TargetType.Pet)) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Target.HasFlag(TargetType.Monster)) { continue; }
|
||||
}
|
||||
|
||||
//do a rough check based on the position of the character's collider first
|
||||
//before the more accurate limb-based check
|
||||
if (Math.Abs(c.WorldPosition.X - detectPos.X) > broadRangeX || Math.Abs(c.WorldPosition.Y - detectPos.Y) > broadRangeY)
|
||||
if (!triggerFromHumans) { continue; }
|
||||
}
|
||||
else if (c.IsPet)
|
||||
{
|
||||
if (!triggerFromPets) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a human or a pet -> monster?
|
||||
if (!triggerFromMonsters) { continue; }
|
||||
if (CharacterParams.CompareGroup(c.Group, CharacterPrefab.HumanGroup))
|
||||
{
|
||||
//characters in the "human" group aren't considered monsters (even if they were something like a friendly mudraptor)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
//do a rough check based on the position of the character's collider first
|
||||
//before the more accurate limb-based check
|
||||
if (Math.Abs(c.WorldPosition.X - detectPos.X) > broadRangeX || Math.Abs(c.WorldPosition.Y - detectPos.Y) > broadRangeY)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.LinearVelocity.LengthSquared() < MinimumVelocity * MinimumVelocity) { continue; }
|
||||
if (MathUtils.CircleIntersectsRectangle(limb.WorldPosition, ConvertUnits.ToDisplayUnits(limb.body.GetMaxExtent()), detectRect))
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.LinearVelocity.LengthSquared() < MinimumVelocity * MinimumVelocity) { continue; }
|
||||
if (MathUtils.CircleIntersectsRectangle(limb.WorldPosition, ConvertUnits.ToDisplayUnits(limb.body.GetMaxExtent()), detectRect))
|
||||
{
|
||||
MotionDetected = true;
|
||||
return;
|
||||
}
|
||||
MotionDetected = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -50,6 +50,9 @@ namespace Barotrauma.Items.Components
|
||||
[InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "Should the component output a value of a capture group instead of a constant signal.", alwaysUseInstanceValues: true)]
|
||||
public bool UseCaptureGroup { get; set; }
|
||||
|
||||
[InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "Should the component output the value of a capture group even if it's empty?", alwaysUseInstanceValues: true)]
|
||||
public bool OutputEmptyCaptureGroup { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("0", IsPropertySaveable.Yes, description: "The signal this item outputs when the received signal does not match the regular expression.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
@@ -120,6 +123,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
string signalOut;
|
||||
bool allowEmptyStringOutput = false;
|
||||
if (previousResult)
|
||||
{
|
||||
if (UseCaptureGroup)
|
||||
@@ -127,6 +131,7 @@ namespace Barotrauma.Items.Components
|
||||
if (previousGroups != null && previousGroups.TryGetValue(Output, out Group group))
|
||||
{
|
||||
signalOut = group.Value;
|
||||
allowEmptyStringOutput = OutputEmptyCaptureGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -143,13 +148,9 @@ namespace Barotrauma.Items.Components
|
||||
signalOut = FalseOutput;
|
||||
}
|
||||
|
||||
if (ContinuousOutput)
|
||||
if (!string.IsNullOrEmpty(signalOut) || (allowEmptyStringOutput && signalOut == string.Empty)) { item.SendSignal(signalOut, "signal_out"); }
|
||||
if (!ContinuousOutput)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(signalOut)) { item.SendSignal(signalOut, "signal_out"); }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(signalOut)) { item.SendSignal(signalOut, "signal_out"); }
|
||||
nonContinuousOutputSent = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the relay currently pass power and signals through it.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(true, IsPropertySaveable.Yes, description: "Can the relay currently pass power and signals through it.", alwaysUseInstanceValues: true)]
|
||||
public bool IsOn
|
||||
{
|
||||
get
|
||||
@@ -139,7 +139,7 @@ namespace Barotrauma.Items.Components
|
||||
isBroken = false;
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime);
|
||||
|
||||
if (Voltage > OverloadVoltage && CanBeOverloaded && item.Repairables.Any())
|
||||
{
|
||||
|
||||
@@ -10,11 +10,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
public readonly string Text;
|
||||
public readonly Color Color;
|
||||
public readonly bool IsWelcomeMessage;
|
||||
|
||||
public TerminalMessage(string text, Color color)
|
||||
public TerminalMessage(string text, Color color, bool isWelcomeMessage)
|
||||
{
|
||||
Text = text;
|
||||
Color = color;
|
||||
IsWelcomeMessage = isWelcomeMessage;
|
||||
}
|
||||
|
||||
public void Deconstruct(out string text, out Color color)
|
||||
@@ -60,7 +62,7 @@ namespace Barotrauma.Items.Components
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) { return; }
|
||||
ShowOnDisplay(value, addToHistory: true, TextColor);
|
||||
ShowOnDisplay(value, addToHistory: true, TextColor, isWelcomeMessage: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +115,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
|
||||
partial void ShowOnDisplay(string input, bool addToHistory, Color color);
|
||||
partial void ShowOnDisplay(string input, bool addToHistory, Color color, bool isWelcomeMessage);
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
{
|
||||
@@ -127,7 +129,7 @@ namespace Barotrauma.Items.Components
|
||||
signal.value = signal.value.Substring(0, MaxMessageLength);
|
||||
}
|
||||
string inputSignal = signal.value.Replace("\\n", "\n");
|
||||
ShowOnDisplay(inputSignal, addToHistory: true, TextColor);
|
||||
ShowOnDisplay(inputSignal, addToHistory: true, TextColor, isWelcomeMessage: false);
|
||||
break;
|
||||
case "set_text_color":
|
||||
if (signal.value != prevColorSignal)
|
||||
@@ -160,7 +162,7 @@ namespace Barotrauma.Items.Components
|
||||
base.OnItemLoaded();
|
||||
if (!DisplayedWelcomeMessage.IsNullOrEmpty() && !WelcomeMessageDisplayed)
|
||||
{
|
||||
ShowOnDisplay(DisplayedWelcomeMessage.Value, addToHistory: !isSubEditor, TextColor);
|
||||
ShowOnDisplay(DisplayedWelcomeMessage.Value, addToHistory: !isSubEditor, TextColor, isWelcomeMessage: true);
|
||||
DisplayedWelcomeMessage = "";
|
||||
//disable welcome message if a game session is running so it doesn't reappear on successive rounds
|
||||
if (GameMain.GameSession != null && !isSubEditor)
|
||||
@@ -175,8 +177,13 @@ namespace Barotrauma.Items.Components
|
||||
var componentElement = base.Save(parentElement);
|
||||
for (int i = 0; i < messageHistory.Count; i++)
|
||||
{
|
||||
componentElement.Add(new XAttribute("msg" + i, messageHistory[i].Text));
|
||||
componentElement.Add(new XAttribute("color" + i, messageHistory[i].Color.ToStringHex()));
|
||||
var msg = messageHistory[i];
|
||||
componentElement.Add(new XAttribute("msg" + i, msg.Text));
|
||||
componentElement.Add(new XAttribute("color" + i, msg.Color.ToStringHex()));
|
||||
if (msg.IsWelcomeMessage)
|
||||
{
|
||||
componentElement.Add(new XAttribute("welcomemessage" + i, true));
|
||||
}
|
||||
}
|
||||
return componentElement;
|
||||
}
|
||||
@@ -189,7 +196,8 @@ namespace Barotrauma.Items.Components
|
||||
string msg = componentElement.GetAttributeString("msg" + i, null);
|
||||
if (msg is null) { break; }
|
||||
Color color = componentElement.GetAttributeColor("color" + i, TextColor);
|
||||
ShowOnDisplay(msg, addToHistory: true, color);
|
||||
bool isWelcomeMessage = componentElement.GetAttributeBool("welcomemessage" + i, false);
|
||||
ShowOnDisplay(msg, addToHistory: true, color, isWelcomeMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public static int GetWaterPercentage(Hull hull)
|
||||
{
|
||||
return hull.WaterVolume > 1.0f ? MathHelper.Clamp((int)Math.Ceiling(hull.WaterPercentage), 0, 100) : 0;
|
||||
//treat less than one pixel of water as "no water"
|
||||
return hull.WaterVolume / hull.Rect.Width > 1.0f ?
|
||||
MathHelper.Clamp((int)Math.Ceiling(hull.WaterPercentage), 0, 100) :
|
||||
0;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
@@ -88,7 +91,7 @@ namespace Barotrauma.Items.Components
|
||||
//item in water -> we definitely want to send the True output
|
||||
isInWater = true;
|
||||
}
|
||||
else if (item.CurrentHull != null && item.CurrentHull.WaterPercentage > 0.0f && item.CurrentHull.WaterVolume > 1.0f)
|
||||
else if (item.CurrentHull != null && GetWaterPercentage(item.CurrentHull) > 0)
|
||||
{
|
||||
//(center of the) item in not water -> check if the water surface is below the bottom of the item's rect
|
||||
if (item.CurrentHull.Surface > item.Rect.Y - item.Rect.Height)
|
||||
|
||||
@@ -89,6 +89,26 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
private float jamTimer;
|
||||
public float JamTimer
|
||||
{
|
||||
get { return jamTimer; }
|
||||
set
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
#if CLIENT
|
||||
if (jamTimer <= 0)
|
||||
{
|
||||
HintManager.OnRadioJammed(Item);
|
||||
}
|
||||
#endif
|
||||
IsActive = true;
|
||||
}
|
||||
jamTimer = Math.Max(0, value);
|
||||
}
|
||||
}
|
||||
|
||||
public WifiComponent(Item item, ContentXElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
@@ -123,8 +143,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanTransmit()
|
||||
public bool CanTransmit(bool ignoreJamming = false)
|
||||
{
|
||||
if (!ignoreJamming)
|
||||
{
|
||||
if (jamTimer > 0) { return false; }
|
||||
}
|
||||
return HasRequiredContainedItems(user: null, addMessage: false);
|
||||
}
|
||||
|
||||
@@ -140,12 +164,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (sender == null || sender.channel != channel) { return false; }
|
||||
if (sender.TeamID != TeamID && !AllowCrossTeamCommunication) { return false; }
|
||||
if (jamTimer > 0) { return false; }
|
||||
|
||||
//if the component is not linked to chat and has nothing connected to the output, sending a signal to it does nothing
|
||||
// = no point in receiving
|
||||
if (!LinkToChat)
|
||||
{
|
||||
if (signalOutConnection == null || signalOutConnection.Wires.Count <= 0)
|
||||
if (signalOutConnection == null || !signalOutConnection.IsConnectedToSomething())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -169,12 +194,16 @@ namespace Barotrauma.Items.Components
|
||||
if (sender == null || sender.channel != channel) { return false; }
|
||||
if (sender.TeamID != TeamID && !AllowCrossTeamCommunication) { return false; }
|
||||
if (Vector2.DistanceSquared(item.WorldPosition, sender.item.WorldPosition) > sender.range * sender.range) { return false; }
|
||||
if (jamTimer > 0) { return false; }
|
||||
return HasRequiredContainedItems(user: null, addMessage: false);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
chatMsgCooldown -= deltaTime;
|
||||
if (chatMsgCooldown <= 0.0f)
|
||||
JamTimer -= deltaTime;
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime);
|
||||
if (chatMsgCooldown <= 0.0f && JamTimer <= 0.0f)
|
||||
{
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,14 @@ namespace Barotrauma.Items.Components
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, "If disabled, the wire will not be dropped when connecting. Used in circuit box to store the wires inside the box.")]
|
||||
public bool DropOnConnect
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Wire(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -224,26 +231,31 @@ namespace Barotrauma.Items.Components
|
||||
connections[connectionIndex] = newConnection;
|
||||
FixNodeEnds();
|
||||
|
||||
if (addNode)
|
||||
if (addNode)
|
||||
{
|
||||
AddNode(newConnection, connectionIndex);
|
||||
}
|
||||
|
||||
SetConnectedDirty();
|
||||
|
||||
if (connections[0] != null && connections[1] != null)
|
||||
if (DropOnConnect)
|
||||
{
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
if (connections[0] != null && connections[1] != null)
|
||||
{
|
||||
if (ic == this) { continue; }
|
||||
ic.Drop(null);
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic == this) { continue; }
|
||||
|
||||
ic.Drop(null);
|
||||
}
|
||||
|
||||
item.Container?.RemoveContained(item);
|
||||
if (item.body != null) { item.body.Enabled = false; }
|
||||
|
||||
IsActive = false;
|
||||
|
||||
CleanNodes();
|
||||
}
|
||||
item.Container?.RemoveContained(item);
|
||||
if (item.body != null) { item.body.Enabled = false; }
|
||||
|
||||
IsActive = false;
|
||||
|
||||
CleanNodes();
|
||||
}
|
||||
|
||||
if (item.body != null) { item.Submarine = newConnection.Item.Submarine; }
|
||||
|
||||
Reference in New Issue
Block a user