(77d1794a) Tester's build January 10th, 2020
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class AdderComponent : ArithmeticComponent
|
||||
{
|
||||
public AdderComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
protected override float Calculate(float signal1, float signal2)
|
||||
{
|
||||
return signal1 + signal2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class AndComponent : ItemComponent
|
||||
{
|
||||
protected string output, falseOutput;
|
||||
|
||||
//an array to keep track of how long ago a non-zero signal was received on both inputs
|
||||
protected float[] timeSinceReceived;
|
||||
|
||||
//the output is sent if both inputs have received a signal within the timeframe
|
||||
protected float timeFrame;
|
||||
|
||||
[InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The item sends the output if both inputs have received a non-zero signal within the timeframe. If set to 0, the inputs must receive a signal at the same time.")]
|
||||
public float TimeFrame
|
||||
{
|
||||
get { return timeFrame; }
|
||||
set
|
||||
{
|
||||
timeFrame = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal sent when both inputs have received a non-zero signal.")]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
set { output = value; }
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The signal sent when both inputs have not received a non-zero signal (if empty, no signal is sent).")]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
set { falseOutput = value; }
|
||||
}
|
||||
|
||||
public AndComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
timeSinceReceived = new float[] { Math.Max(timeFrame * 2.0f, 0.1f), Math.Max(timeFrame * 2.0f, 0.1f) };
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
bool sendOutput = true;
|
||||
for (int i = 0; i < timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] > timeFrame) sendOutput = false;
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
string signalOut = sendOutput ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut)) return;
|
||||
|
||||
item.SendSignal(0, signalOut, "signal_out", null);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_in1":
|
||||
if (signal == "0") return;
|
||||
timeSinceReceived[0] = 0.0f;
|
||||
break;
|
||||
case "signal_in2":
|
||||
if (signal == "0") return;
|
||||
timeSinceReceived[1] = 0.0f;
|
||||
break;
|
||||
case "set_output":
|
||||
output = signal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
abstract class ArithmeticComponent : ItemComponent
|
||||
{
|
||||
//an array to keep track of how long ago a signal was received on both inputs
|
||||
protected float[] timeSinceReceived;
|
||||
|
||||
protected float[] receivedSignal;
|
||||
|
||||
//the output is sent if both inputs have received a signal within the timeframe
|
||||
protected float timeFrame;
|
||||
|
||||
[Serialize(999999.0f, true, description: "The output of the item is restricted below this value."),
|
||||
InGameEditable(MinValueFloat = -999999.0f, MaxValueFloat = 999999.0f)]
|
||||
public float ClampMax
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(-999999.0f, true, description: "The output of the item is restricted above this value."),
|
||||
InGameEditable(MinValueFloat = -999999.0f, MaxValueFloat = 999999.0f)]
|
||||
public float ClampMin
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[InGameEditable(DecimalCount = 2),
|
||||
Serialize(0.0f, true, description: "The item must have received signals to both inputs within this timeframe to output the sum of the signals." +
|
||||
" If set to 0, the inputs must be received at the same time.")]
|
||||
public float TimeFrame
|
||||
{
|
||||
get { return timeFrame; }
|
||||
set
|
||||
{
|
||||
timeFrame = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
public ArithmeticComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
timeSinceReceived = new float[] { Math.Max(timeFrame * 2.0f, 0.1f), Math.Max(timeFrame * 2.0f, 0.1f) };
|
||||
receivedSignal = new float[2];
|
||||
}
|
||||
|
||||
sealed public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
for (int i = 0; i < timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] > timeFrame)
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
float output = Calculate(receivedSignal[0], receivedSignal[1]);
|
||||
item.SendSignal(0, MathHelper.Clamp(output, ClampMin, ClampMax).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
|
||||
protected abstract float Calculate(float signal1, float signal2);
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_in1":
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[0]);
|
||||
timeSinceReceived[0] = 0.0f;
|
||||
IsActive = true;
|
||||
break;
|
||||
case "signal_in2":
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[1]);
|
||||
timeSinceReceived[1] = 0.0f;
|
||||
IsActive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class ColorComponent : ItemComponent
|
||||
{
|
||||
protected float[] receivedSignal;
|
||||
|
||||
private string output = "0,0,0,0";
|
||||
|
||||
public ColorComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
receivedSignal = new float[4];
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
item.SendSignal(0, output, "signal_out", null);
|
||||
}
|
||||
|
||||
private void UpdateOutput()
|
||||
{
|
||||
output = receivedSignal[0].ToString("G", CultureInfo.InvariantCulture);
|
||||
output += "," + receivedSignal[1].ToString("G", CultureInfo.InvariantCulture);
|
||||
output += "," + receivedSignal[2].ToString("G", CultureInfo.InvariantCulture);
|
||||
output += "," + receivedSignal[3].ToString("G", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_r":
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[0]);
|
||||
UpdateOutput();
|
||||
break;
|
||||
case "signal_g":
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[1]);
|
||||
UpdateOutput();
|
||||
break;
|
||||
case "signal_b":
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[2]);
|
||||
UpdateOutput();
|
||||
break;
|
||||
case "signal_a":
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[3]);
|
||||
UpdateOutput();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Connection
|
||||
{
|
||||
//how many wires can be linked to a single connector
|
||||
public const int MaxLinked = 5;
|
||||
|
||||
public readonly string Name;
|
||||
public readonly string DisplayName;
|
||||
|
||||
private Wire[] wires;
|
||||
public IEnumerable<Wire> Wires
|
||||
{
|
||||
get { return wires; }
|
||||
}
|
||||
|
||||
private Item item;
|
||||
|
||||
public readonly bool IsOutput;
|
||||
|
||||
public readonly List<StatusEffect> Effects;
|
||||
|
||||
public readonly ushort[] wireId;
|
||||
|
||||
public bool IsPower
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private bool recipientsDirty = true;
|
||||
private List<Connection> recipients = new List<Connection>();
|
||||
public List<Connection> Recipients
|
||||
{
|
||||
get
|
||||
{
|
||||
if (recipientsDirty) RefreshRecipients();
|
||||
return recipients;
|
||||
}
|
||||
}
|
||||
|
||||
public Item Item
|
||||
{
|
||||
get { return item; }
|
||||
}
|
||||
|
||||
public ConnectionPanel ConnectionPanel
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Connection (" + item.Name + ", " + Name + ")";
|
||||
}
|
||||
|
||||
public Connection(XElement element, ConnectionPanel connectionPanel)
|
||||
{
|
||||
|
||||
#if CLIENT
|
||||
if (connector == null)
|
||||
{
|
||||
connector = GUI.Style.GetComponentStyle("ConnectionPanelConnector").Sprites[GUIComponent.ComponentState.None][0].Sprite;
|
||||
wireVertical = GUI.Style.GetComponentStyle("ConnectionPanelWire").Sprites[GUIComponent.ComponentState.None][0].Sprite;
|
||||
connectionSprite = GUI.Style.GetComponentStyle("ConnectionPanelConnection").Sprites[GUIComponent.ComponentState.None][0].Sprite;
|
||||
connectionSpriteHighlight = GUI.Style.GetComponentStyle("ConnectionPanelConnection").Sprites[GUIComponent.ComponentState.Hover][0].Sprite;
|
||||
screwSprites = GUI.Style.GetComponentStyle("ConnectionPanelScrew").Sprites[GUIComponent.ComponentState.None].Select(s => s.Sprite).ToList();
|
||||
}
|
||||
#endif
|
||||
ConnectionPanel = connectionPanel;
|
||||
item = connectionPanel.Item;
|
||||
|
||||
wires = new Wire[MaxLinked];
|
||||
|
||||
IsOutput = element.Name.ToString() == "output";
|
||||
Name = element.GetAttributeString("name", IsOutput ? "output" : "input");
|
||||
|
||||
string displayNameTag = "", fallbackTag = "";
|
||||
//if displayname is not present, attempt to find it from the prefab
|
||||
if (element.Attribute("displayname") == null)
|
||||
{
|
||||
foreach (XElement subElement in item.Prefab.ConfigElement.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "connectionpanel") { continue; }
|
||||
|
||||
foreach (XElement connectionElement in subElement.Elements())
|
||||
{
|
||||
string prefabConnectionName = element.GetAttributeString("name", null);
|
||||
if (prefabConnectionName == Name)
|
||||
{
|
||||
displayNameTag = connectionElement.GetAttributeString("displayname", "");
|
||||
fallbackTag = connectionElement.GetAttributeString("fallbackdisplayname", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
displayNameTag = element.GetAttributeString("displayname", "");
|
||||
fallbackTag = element.GetAttributeString("fallbackdisplayname", null);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(displayNameTag))
|
||||
{
|
||||
//extract the tag parts in case the tags contains variables
|
||||
string tagWithoutVariables = displayNameTag?.Split('~')?.FirstOrDefault();
|
||||
string fallbackTagWithoutVariables = fallbackTag?.Split('~')?.FirstOrDefault();
|
||||
//use displayNameTag if found, otherwise fallBack
|
||||
if (TextManager.ContainsTag(tagWithoutVariables))
|
||||
{
|
||||
DisplayName = TextManager.GetServerMessage(displayNameTag);
|
||||
}
|
||||
else if (TextManager.ContainsTag(fallbackTagWithoutVariables))
|
||||
{
|
||||
DisplayName = TextManager.GetServerMessage(fallbackTag);
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(DisplayName))
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Missing display name in connection " + item.Name + ": " + Name);
|
||||
#endif
|
||||
DisplayName = Name;
|
||||
}
|
||||
|
||||
IsPower = Name == "power_in" || Name == "power" || Name == "power_out";
|
||||
|
||||
Effects = new List<StatusEffect>();
|
||||
|
||||
wireId = new ushort[MaxLinked];
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "link":
|
||||
int index = -1;
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wireId[i] < 1) index = i;
|
||||
}
|
||||
if (index == -1) break;
|
||||
|
||||
int id = subElement.GetAttributeInt("w", 0);
|
||||
if (id < 0) id = 0;
|
||||
wireId[index] = (ushort)id;
|
||||
|
||||
break;
|
||||
|
||||
case "statuseffect":
|
||||
Effects.Add(StatusEffect.Load(subElement, item.Name + ", connection " + Name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshRecipients()
|
||||
{
|
||||
recipients.Clear();
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == null) continue;
|
||||
Connection recipient = wires[i].OtherConnection(this);
|
||||
if (recipient != null) recipients.Add(recipient);
|
||||
}
|
||||
recipientsDirty = false;
|
||||
}
|
||||
|
||||
public int FindEmptyIndex()
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == null) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int FindWireIndex(Wire wire)
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == wire) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int FindWireIndex(Item wireItem)
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == null && wireItem == null) return i;
|
||||
if (wires[i] != null && wires[i].Item == wireItem) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void TryAddLink(Wire wire)
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == null)
|
||||
{
|
||||
SetWire(i, wire);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetWire(int index, Wire wire)
|
||||
{
|
||||
Wire previousWire = wires[index];
|
||||
if (wire != previousWire && previousWire != null)
|
||||
{
|
||||
var otherConnection = previousWire.OtherConnection(this);
|
||||
if (otherConnection != null)
|
||||
{
|
||||
otherConnection.recipientsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
wires[index] = wire;
|
||||
recipientsDirty = true;
|
||||
if (wire != null)
|
||||
{
|
||||
ConnectionPanel.DisconnectedWires.Remove(wire);
|
||||
var otherConnection = wire.OtherConnection(this);
|
||||
if (otherConnection != null)
|
||||
{
|
||||
otherConnection.recipientsDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SendSignal(int stepsTaken, string signal, Item source, Character sender, float power, float signalStrength = 1.0f)
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == null) { continue; }
|
||||
|
||||
Connection recipient = wires[i].OtherConnection(this);
|
||||
if (recipient == null) { continue; }
|
||||
if (recipient.item == this.item || recipient.item == source) { continue; }
|
||||
|
||||
source?.LastSentSignalRecipients.Add(recipient.item);
|
||||
|
||||
foreach (ItemComponent ic in recipient.item.Components)
|
||||
{
|
||||
ic.ReceiveSignal(stepsTaken, signal, recipient, source, sender, power, signalStrength);
|
||||
}
|
||||
|
||||
foreach (StatusEffect effect in recipient.Effects)
|
||||
{
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SendPowerProbeSignal(Item source, float power)
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == null) { continue; }
|
||||
|
||||
Connection recipient = wires[i].OtherConnection(this);
|
||||
if (recipient == null) { continue; }
|
||||
|
||||
recipient.item.GetComponent<Powered>()?.ReceivePowerProbeSignal(recipient, source, power);
|
||||
}
|
||||
}
|
||||
public void ClearConnections()
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == null) continue;
|
||||
|
||||
wires[i].RemoveConnection(this);
|
||||
wires[i] = null;
|
||||
recipientsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void ConnectLinked()
|
||||
{
|
||||
if (wireId == null) return;
|
||||
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wireId[i] == 0) { continue; }
|
||||
|
||||
if (!(Entity.FindEntityByID(wireId[i]) is Item wireItem)) { continue; }
|
||||
wires[i] = wireItem.GetComponent<Wire>();
|
||||
recipientsDirty = true;
|
||||
|
||||
if (wires[i] != null)
|
||||
{
|
||||
if (wires[i].Item.body != null) wires[i].Item.body.Enabled = false;
|
||||
wires[i].Connect(this, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Save(XElement parentElement)
|
||||
{
|
||||
XElement newElement = new XElement(IsOutput ? "output" : "input", new XAttribute("name", Name));
|
||||
|
||||
Array.Sort(wires, delegate (Wire wire1, Wire wire2)
|
||||
{
|
||||
if (wire1 == null) return 1;
|
||||
if (wire2 == null) return -1;
|
||||
return wire1.Item.ID.CompareTo(wire2.Item.ID);
|
||||
});
|
||||
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == null) continue;
|
||||
|
||||
newElement.Add(new XElement("link",
|
||||
new XAttribute("w", wires[i].Item.ID.ToString())));
|
||||
}
|
||||
|
||||
parentElement.Add(newElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class ConnectionPanel : ItemComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
public List<Connection> Connections;
|
||||
|
||||
private Character user;
|
||||
|
||||
/// <summary>
|
||||
/// Wires that have been disconnected from the panel, but not removed completely (visible at the bottom of the connection panel).
|
||||
/// </summary>
|
||||
public readonly HashSet<Wire> DisconnectedWires = new HashSet<Wire>();
|
||||
|
||||
private List<ushort> disconnectedWireIds;
|
||||
|
||||
[Editable, Serialize(false, true, description: "Locked connection panels cannot be rewired in-game.")]
|
||||
public bool Locked
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
//connection panels can't be deactivated externally (by signals or status effects)
|
||||
public override bool IsActive
|
||||
{
|
||||
get { return base.IsActive; }
|
||||
set { /*do nothing*/ }
|
||||
}
|
||||
|
||||
public Character User
|
||||
{
|
||||
get { return user; }
|
||||
}
|
||||
|
||||
public ConnectionPanel(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
Connections = new List<Connection>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString())
|
||||
{
|
||||
case "input":
|
||||
Connections.Add(new Connection(subElement, this));
|
||||
break;
|
||||
case "output":
|
||||
Connections.Add(new Connection(subElement, this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
base.IsActive = true;
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
foreach (Connection c in Connections)
|
||||
{
|
||||
c.ConnectLinked();
|
||||
}
|
||||
|
||||
if (disconnectedWireIds != null)
|
||||
{
|
||||
foreach (ushort disconnectedWireId in disconnectedWireIds)
|
||||
{
|
||||
if (!(Entity.FindEntityByID(disconnectedWireId) is Item wireItem)) { continue; }
|
||||
Wire wire = wireItem.GetComponent<Wire>();
|
||||
if (wire != null)
|
||||
{
|
||||
DisconnectedWires.Add(wire);
|
||||
base.IsActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
if (item.body != null)
|
||||
{
|
||||
var holdable = item.GetComponent<Holdable>();
|
||||
if (holdable == null || !holdable.Attachable)
|
||||
{
|
||||
DebugConsole.ThrowError("Item \"" + item.Name + "\" has a ConnectionPanel component," +
|
||||
" but cannot be wired because it has an active physics body that cannot be attached to a wall." +
|
||||
" Remove the physics body or add a Holdable component with the Attachable attribute set to true.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveConnectedWires(Vector2 amount)
|
||||
{
|
||||
Vector2 wireNodeOffset = item.Submarine == null ? Vector2.Zero : item.Submarine.HiddenSubPosition + amount;
|
||||
foreach (Connection c in Connections)
|
||||
{
|
||||
foreach (Wire wire in c.Wires)
|
||||
{
|
||||
if (wire == null) continue;
|
||||
#if CLIENT
|
||||
if (wire.Item.IsSelected) continue;
|
||||
#endif
|
||||
var wireNodes = wire.GetNodes();
|
||||
if (wireNodes.Count == 0) continue;
|
||||
|
||||
if (Submarine.RectContains(item.Rect, wireNodes[0] + wireNodeOffset))
|
||||
{
|
||||
wire.MoveNode(0, amount);
|
||||
}
|
||||
else if (Submarine.RectContains(item.Rect, wireNodes[wireNodes.Count - 1] + wireNodeOffset))
|
||||
{
|
||||
wire.MoveNode(wireNodes.Count - 1, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
UpdateProjSpecific(deltaTime);
|
||||
|
||||
if (user == null || user.SelectedConstruction != item)
|
||||
{
|
||||
#if SERVER
|
||||
if (user != null) { item.CreateServerEvent(this); }
|
||||
#endif
|
||||
user = null;
|
||||
if (DisconnectedWires.Count == 0) { base.IsActive = false; }
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user.Enabled || !HasRequiredItems(user, addMessage: false))
|
||||
{
|
||||
user = null;
|
||||
base.IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
user.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * (((float)Timing.TotalTime / 10.0f) % 0.1f));
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
Update(deltaTime, cam);
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public override bool Select(Character picker)
|
||||
{
|
||||
//attaching wires to items with a body is not allowed
|
||||
//(signal items remove their bodies when attached to a wall)
|
||||
if (item.body != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
user = picker;
|
||||
#if SERVER
|
||||
if (user != null) { item.CreateServerEvent(this); }
|
||||
#endif
|
||||
base.IsActive = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || character != user) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the character manages to succesfully rewire the panel, and if not, apply OnFailure effects
|
||||
/// </summary>
|
||||
public bool CheckCharacterSuccess(Character character)
|
||||
{
|
||||
if (character == null) { return false; }
|
||||
|
||||
var powered = item.GetComponent<Powered>();
|
||||
if (powered != null)
|
||||
{
|
||||
//unpowered panels can be rewired without a risk of electrical shock
|
||||
if (powered.Voltage < 0.1f) { return true; }
|
||||
}
|
||||
|
||||
float degreeOfSuccess = DegreeOfSuccess(character);
|
||||
if (Rand.Range(0.0f, 0.5f) < degreeOfSuccess) { return true; }
|
||||
|
||||
item.ApplyStatusEffects(ActionType.OnFailure, 1.0f, character);
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Load(XElement element, bool usePrefabValues)
|
||||
{
|
||||
base.Load(element, usePrefabValues);
|
||||
|
||||
List<Connection> loadedConnections = new List<Connection>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString())
|
||||
{
|
||||
case "input":
|
||||
loadedConnections.Add(new Connection(subElement, this));
|
||||
break;
|
||||
case "output":
|
||||
loadedConnections.Add(new Connection(subElement, this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < loadedConnections.Count && i < Connections.Count; i++)
|
||||
{
|
||||
loadedConnections[i].wireId.CopyTo(Connections[i].wireId, 0);
|
||||
}
|
||||
|
||||
disconnectedWireIds = element.GetAttributeUshortArray("disconnectedwires", new ushort[0]).ToList();
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
XElement componentElement = base.Save(parentElement);
|
||||
|
||||
foreach (Connection c in Connections)
|
||||
{
|
||||
c.Save(componentElement);
|
||||
}
|
||||
|
||||
if (DisconnectedWires.Count > 0)
|
||||
{
|
||||
componentElement.Add(new XAttribute("disconnectedwires", string.Join(",", DisconnectedWires.Select(w => w.Item.ID))));
|
||||
}
|
||||
|
||||
return componentElement;
|
||||
}
|
||||
|
||||
protected override void ShallowRemoveComponentSpecific()
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
foreach (Wire wire in DisconnectedWires.ToList())
|
||||
{
|
||||
if (wire.OtherConnection(null) == null) //wire not connected to anything else
|
||||
{
|
||||
wire.Item.Drop(null);
|
||||
}
|
||||
}
|
||||
|
||||
DisconnectedWires.Clear();
|
||||
foreach (Connection c in Connections)
|
||||
{
|
||||
foreach (Wire wire in c.Wires)
|
||||
{
|
||||
if (wire == null) { continue; }
|
||||
|
||||
if (wire.OtherConnection(c) == null) //wire not connected to anything else
|
||||
{
|
||||
wire.Item.Drop(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
wire.RemoveConnection(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
rewireSoundChannel?.FadeOutAndDispose();
|
||||
rewireSoundChannel = null;
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
{
|
||||
#if CLIENT
|
||||
TriggerRewiringSound();
|
||||
#endif
|
||||
|
||||
foreach (Connection connection in Connections)
|
||||
{
|
||||
foreach (Wire wire in connection.Wires)
|
||||
{
|
||||
msg.Write(wire?.Item == null ? (ushort)0 : wire.Item.ID);
|
||||
}
|
||||
}
|
||||
|
||||
msg.Write((ushort)DisconnectedWires.Count());
|
||||
foreach (Wire disconnectedWire in DisconnectedWires)
|
||||
{
|
||||
msg.Write(disconnectedWire.Item.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class CustomInterface : ItemComponent, IClientSerializable, IServerSerializable
|
||||
{
|
||||
class CustomInterfaceElement : ISerializableEntity
|
||||
{
|
||||
public bool ContinuousSignal;
|
||||
public bool State;
|
||||
public string ConnectionName;
|
||||
public Connection Connection;
|
||||
[Serialize("", false, translationTextTag: "Label.", description: "The text displayed on this button/tickbox."), Editable]
|
||||
public string Label { get; set; }
|
||||
[Serialize("1", false, description: "The signal sent out when this button is pressed or this tickbox checked."), Editable]
|
||||
public string Signal { get; set; }
|
||||
|
||||
public string Name => "CustomInterfaceElement";
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; set; }
|
||||
|
||||
public List<StatusEffect> StatusEffects = new List<StatusEffect>();
|
||||
|
||||
public CustomInterfaceElement(XElement element)
|
||||
{
|
||||
Label = element.GetAttributeString("text", "");
|
||||
ConnectionName = element.GetAttributeString("connection", "");
|
||||
Signal = element.GetAttributeString("signal", "1");
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLowerInvariant() == "statuseffect")
|
||||
{
|
||||
StatusEffects.Add(StatusEffect.Load(subElement, parentDebugName: "custom interface element (label " + Label + ")"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string[] labels;
|
||||
[Serialize("", true, description: "The texts displayed on the buttons/tickboxes, separated by commas.")]
|
||||
public string Labels
|
||||
{
|
||||
get { return string.Join(",", labels); }
|
||||
set
|
||||
{
|
||||
if (value == null) { return; }
|
||||
string[] splitValues = value == "" ? new string[0] : value.Split(',');
|
||||
if (customInterfaceElementList.Count > 0)
|
||||
{
|
||||
UpdateLabels(splitValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
private string[] signals;
|
||||
[Serialize("", true, description: "The signals sent when the buttons are pressed or the tickboxes checked, separated by commas.")]
|
||||
public string Signals
|
||||
{
|
||||
//use semicolon as a separator because comma may be needed in the signals (for color or vector values for example)
|
||||
//kind of hacky, we should probably add support for (string) arrays to SerializableEntityEditor so this wouldn't be needed
|
||||
get { return signals == null ? "" : string.Join(";", signals); }
|
||||
set
|
||||
{
|
||||
if (value == null) { return; }
|
||||
string[] splitValues = value == "" ? new string[0] : value.Split(';');
|
||||
if (customInterfaceElementList.Count > 0)
|
||||
{
|
||||
signals = new string[customInterfaceElementList.Count];
|
||||
for (int i = 0; i < customInterfaceElementList.Count; i++)
|
||||
{
|
||||
signals[i] = i < splitValues.Length ? splitValues[i] : customInterfaceElementList[i].Signal;
|
||||
customInterfaceElementList[i].Signal = signals[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<CustomInterfaceElement> customInterfaceElementList = new List<CustomInterfaceElement>();
|
||||
|
||||
public CustomInterface(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "button":
|
||||
var button = new CustomInterfaceElement(subElement)
|
||||
{
|
||||
ContinuousSignal = false
|
||||
};
|
||||
if (string.IsNullOrEmpty(button.Label))
|
||||
{
|
||||
button.Label = "Signal out " + customInterfaceElementList.Count(e => !e.ContinuousSignal);
|
||||
}
|
||||
customInterfaceElementList.Add(button);
|
||||
break;
|
||||
case "tickbox":
|
||||
var tickBox = new CustomInterfaceElement(subElement)
|
||||
{
|
||||
ContinuousSignal = true
|
||||
};
|
||||
if (string.IsNullOrEmpty(tickBox.Label))
|
||||
{
|
||||
tickBox.Label = "Signal out " + customInterfaceElementList.Count(e => !e.ContinuousSignal);
|
||||
}
|
||||
customInterfaceElementList.Add(tickBox);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
IsActive = true;
|
||||
InitProjSpecific(element);
|
||||
Labels = element.GetAttributeString("labels", "");
|
||||
Signals = element.GetAttributeString("signals", "");
|
||||
}
|
||||
|
||||
private void UpdateLabels(string[] newLabels)
|
||||
{
|
||||
labels = new string[customInterfaceElementList.Count];
|
||||
for (int i = 0; i < labels.Length; i++)
|
||||
{
|
||||
labels[i] = i < newLabels.Length ? newLabels[i] : customInterfaceElementList[i].Label;
|
||||
if (Screen.Selected != GameMain.SubEditorScreen)
|
||||
{
|
||||
customInterfaceElementList[i].Label = TextManager.Get(labels[i], returnNull: true) ?? labels[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
customInterfaceElementList[i].Label = labels[i];
|
||||
}
|
||||
}
|
||||
UpdateLabelsProjSpecific();
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
foreach (CustomInterfaceElement ciElement in customInterfaceElementList)
|
||||
{
|
||||
ciElement.Connection = item.Connections?.FirstOrDefault(c => c.Name == ciElement.ConnectionName);
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateLabelsProjSpecific();
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
|
||||
private void ButtonClicked(CustomInterfaceElement btnElement)
|
||||
{
|
||||
if (btnElement == null) return;
|
||||
if (btnElement.Connection != null)
|
||||
{
|
||||
item.SendSignal(0, btnElement.Signal, btnElement.Connection, sender: null, source: item);
|
||||
}
|
||||
foreach (StatusEffect effect in btnElement.StatusEffects)
|
||||
{
|
||||
item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private void TickBoxToggled(CustomInterfaceElement tickBoxElement, bool state)
|
||||
{
|
||||
if (tickBoxElement == null) { return; }
|
||||
tickBoxElement.State = state;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
UpdateProjSpecific();
|
||||
foreach (CustomInterfaceElement ciElement in customInterfaceElementList)
|
||||
{
|
||||
if (!ciElement.ContinuousSignal) { continue; }
|
||||
//TODO: allow changing output when a tickbox is not selected
|
||||
if (!string.IsNullOrEmpty(ciElement.Signal) && ciElement.Connection != null)
|
||||
{
|
||||
item.SendSignal(0, ciElement.State ? ciElement.Signal : "0", ciElement.Connection, sender: null, source: item);
|
||||
}
|
||||
|
||||
foreach (StatusEffect effect in ciElement.StatusEffects)
|
||||
{
|
||||
item.ApplyStatusEffect(effect, ciElement.State ? ActionType.OnUse : ActionType.OnSecondaryUse, 1.0f, null, null, null, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific();
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
labels = customInterfaceElementList.Select(ci => ci.Label).ToArray();
|
||||
signals = customInterfaceElementList.Select(ci => ci.Signal).ToArray();
|
||||
return base.Save(parentElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class DelayComponent : ItemComponent
|
||||
{
|
||||
class DelayedSignal
|
||||
{
|
||||
public readonly string Signal;
|
||||
public readonly float SignalStrength;
|
||||
//in number of frames
|
||||
public int SendTimer;
|
||||
//in number of frames
|
||||
public int SendDuration;
|
||||
|
||||
public DelayedSignal(string signal, float signalStrength, int sendTimer)
|
||||
{
|
||||
Signal = signal;
|
||||
SignalStrength = signalStrength;
|
||||
SendTimer = sendTimer;
|
||||
}
|
||||
}
|
||||
|
||||
private int signalQueueSize;
|
||||
private int delayTicks;
|
||||
|
||||
private Queue<DelayedSignal> signalQueue;
|
||||
|
||||
private DelayedSignal prevQueuedSignal;
|
||||
|
||||
private float delay;
|
||||
[InGameEditable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f, DecimalCount = 2), Serialize(1.0f, true, description: "How long the item delays the signals (in seconds).")]
|
||||
public float Delay
|
||||
{
|
||||
get { return delay; }
|
||||
set
|
||||
{
|
||||
if (value == delay) { return; }
|
||||
delay = value;
|
||||
delayTicks = (int)(delay / Timing.Step);
|
||||
signalQueueSize = delayTicks * 2;
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "Should the component discard previously received signals when a new one is received.")]
|
||||
public bool ResetWhenSignalReceived
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "Should the component discard previously received signals when the incoming signal changes.")]
|
||||
public bool ResetWhenDifferentSignalReceived
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DelayComponent(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
signalQueue = new Queue<DelayedSignal>();
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
foreach (var val in signalQueue)
|
||||
{
|
||||
val.SendTimer -= 1;
|
||||
}
|
||||
|
||||
while (signalQueue.Count > 0 && signalQueue.Peek().SendTimer <= 0)
|
||||
{
|
||||
var signalOut = signalQueue.Peek();
|
||||
signalOut.SendDuration -= 1;
|
||||
item.SendSignal(0, signalOut.Signal, "signal_out", null, signalStrength: signalOut.SignalStrength);
|
||||
if (signalOut.SendDuration <= 0) { signalQueue.Dequeue(); } else { break; }
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_in":
|
||||
if (signalQueue.Count >= signalQueueSize) { return; }
|
||||
if (ResetWhenSignalReceived) { prevQueuedSignal = null; signalQueue.Clear(); }
|
||||
if (ResetWhenDifferentSignalReceived && signalQueue.Count > 0 && signalQueue.Peek().Signal != signal)
|
||||
{
|
||||
prevQueuedSignal = null;
|
||||
signalQueue.Clear();
|
||||
}
|
||||
|
||||
if (prevQueuedSignal != null &&
|
||||
prevQueuedSignal.Signal == signal &&
|
||||
MathUtils.NearlyEqual(prevQueuedSignal.SignalStrength, signalStrength) &&
|
||||
((prevQueuedSignal.SendTimer + prevQueuedSignal.SendDuration == delayTicks) || (prevQueuedSignal.SendTimer <= 0 && prevQueuedSignal.SendDuration > 0)))
|
||||
{
|
||||
prevQueuedSignal.SendDuration += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
prevQueuedSignal = new DelayedSignal(signal, signalStrength, delayTicks)
|
||||
{
|
||||
SendDuration = 1
|
||||
};
|
||||
signalQueue.Enqueue(prevQueuedSignal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class DivideComponent : ArithmeticComponent
|
||||
{
|
||||
public DivideComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
protected override float Calculate(float signal1, float signal2)
|
||||
{
|
||||
return signal1 / signal2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class EqualsComponent : ItemComponent
|
||||
{
|
||||
protected string output, falseOutput;
|
||||
|
||||
//an array to keep track of how long ago a signal was received on both inputs
|
||||
protected float[] timeSinceReceived;
|
||||
|
||||
protected string[] receivedSignal;
|
||||
|
||||
//the output is sent if both inputs have received a signal within the timeframe
|
||||
protected float timeFrame;
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signals are equal.")]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
set { output = value; }
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The signal this item outputs when the received signals are not equal.")]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
set { falseOutput = value; }
|
||||
}
|
||||
|
||||
[InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The maximum amount of time between the received signals. If set to 0, the signals must be received at the same time.")]
|
||||
public float TimeFrame
|
||||
{
|
||||
get { return timeFrame; }
|
||||
set
|
||||
{
|
||||
timeFrame = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
public EqualsComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
timeSinceReceived = new float[] { Math.Max(timeFrame * 2.0f, 0.1f), Math.Max(timeFrame * 2.0f, 0.1f) };
|
||||
receivedSignal = new string[2];
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
bool sendOutput = false;
|
||||
for (int i = 0; i < timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] <= timeFrame) sendOutput = true;
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
if (sendOutput)
|
||||
{
|
||||
string signalOut = receivedSignal[0] == receivedSignal[1] ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut)) return;
|
||||
|
||||
item.SendSignal(0, signalOut, "signal_out", null);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_in1":
|
||||
receivedSignal[0] = signal;
|
||||
timeSinceReceived[0] = 0.0f;
|
||||
break;
|
||||
case "signal_in2":
|
||||
receivedSignal[1] = signal;
|
||||
timeSinceReceived[1] = 0.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class ExponentiationComponent : ItemComponent
|
||||
{
|
||||
private float exponent;
|
||||
[InGameEditable, Serialize(1.0f, false, description: "The exponent of the operation.")]
|
||||
public float Exponent
|
||||
{
|
||||
get
|
||||
{
|
||||
return exponent;
|
||||
}
|
||||
set
|
||||
{
|
||||
exponent = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ExponentiationComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "set_exponent":
|
||||
case "exponent":
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out exponent);
|
||||
break;
|
||||
case "signal_in":
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float value);
|
||||
item.SendSignal(0, MathUtils.Pow(value, Exponent).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class FunctionComponent : ItemComponent
|
||||
{
|
||||
public enum FunctionType
|
||||
{
|
||||
Round,
|
||||
Ceil,
|
||||
Floor,
|
||||
Factorial,
|
||||
AbsoluteValue,
|
||||
SquareRoot
|
||||
}
|
||||
|
||||
[Serialize(FunctionType.Round, false, description: "Which kind of function to run the input through.")]
|
||||
public FunctionType Function
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public FunctionComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
if (connection.Name != "signal_in") return;
|
||||
if (!float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float value)) return;
|
||||
switch (Function)
|
||||
{
|
||||
case FunctionType.Round:
|
||||
item.SendSignal(0, Math.Round(value).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Ceil:
|
||||
item.SendSignal(0, Math.Ceiling(value).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Floor:
|
||||
item.SendSignal(0, Math.Floor(value).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Factorial:
|
||||
int intVal = (int)Math.Min(value, 20);
|
||||
ulong factorial = 1;
|
||||
for (int i = intVal; i > 0; i--)
|
||||
{
|
||||
factorial *= (ulong)i;
|
||||
}
|
||||
item.SendSignal(0, factorial.ToString(), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.AbsoluteValue:
|
||||
item.SendSignal(0, Math.Abs(value).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.SquareRoot:
|
||||
double square = value > 0 ? Math.Sqrt(value) : 0;
|
||||
item.SendSignal(0, square.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Function {Function} has not been implemented.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class GreaterComponent : EqualsComponent
|
||||
{
|
||||
private float val1, val2;
|
||||
|
||||
public GreaterComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
bool sendOutput = false;
|
||||
for (int i = 0; i < timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] <= timeFrame) sendOutput = true;
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
if (sendOutput)
|
||||
{
|
||||
string signalOut = val1 > val2 ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut)) return;
|
||||
|
||||
item.SendSignal(0, signalOut, "signal_out", null);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
base.ReceiveSignal(stepsTaken, signal, connection, source, sender, power, signalStrength);
|
||||
float.TryParse(receivedSignal[0], NumberStyles.Float, CultureInfo.InvariantCulture, out val1);
|
||||
float.TryParse(receivedSignal[1], NumberStyles.Float, CultureInfo.InvariantCulture, out val2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Networking;
|
||||
#if CLIENT
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Barotrauma.Lights;
|
||||
#endif
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class LightComponent : Powered, IServerSerializable, IDrawableComponent
|
||||
{
|
||||
private Color lightColor;
|
||||
private float lightBrightness;
|
||||
private float blinkFrequency;
|
||||
private float range;
|
||||
private float flicker;
|
||||
private bool castShadows;
|
||||
private bool drawBehindSubs;
|
||||
|
||||
private float blinkTimer;
|
||||
|
||||
private bool itemLoaded;
|
||||
|
||||
public PhysicsBody ParentBody;
|
||||
|
||||
[Serialize(100.0f, true, description: "The range of the emitted light. Higher values are more performance-intensive."),
|
||||
Editable(MinValueFloat = 0.0f, MaxValueFloat = 2048.0f)]
|
||||
public float Range
|
||||
{
|
||||
get { return range; }
|
||||
set
|
||||
{
|
||||
range = MathHelper.Clamp(value, 0.0f, 4096.0f);
|
||||
#if CLIENT
|
||||
if (light != null) { light.Range = range; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public float Rotation;
|
||||
|
||||
[Editable, Serialize(true, true, description: "Should structures cast shadows when light from this light source hits them. " +
|
||||
"Disabling shadows increases the performance of the game, and is recommended for lights with a short range.")]
|
||||
public bool CastShadows
|
||||
{
|
||||
get { return castShadows; }
|
||||
set
|
||||
{
|
||||
castShadows = value;
|
||||
#if CLIENT
|
||||
if (light != null) light.CastShadows = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Lights drawn behind submarines don't cast any shadows and are much faster to draw than shadow-casting lights. " +
|
||||
"It's recommended to enable this on decorative lights outside the submarine's hull.")]
|
||||
public bool DrawBehindSubs
|
||||
{
|
||||
get { return drawBehindSubs; }
|
||||
set
|
||||
{
|
||||
drawBehindSubs = value;
|
||||
#if CLIENT
|
||||
if (light != null) light.IsBackground = drawBehindSubs;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Is the light currently on.")]
|
||||
public bool IsOn
|
||||
{
|
||||
get { return IsActive; }
|
||||
set
|
||||
{
|
||||
if (IsActive == value) { return; }
|
||||
|
||||
IsActive = value;
|
||||
#if SERVER
|
||||
if (GameMain.Server != null && itemLoaded) { item.CreateServerEvent(this); }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "How heavily the light flickers. 0 = no flickering, 1 = the light will alternate between completely dark and full brightness.")]
|
||||
public float Flicker
|
||||
{
|
||||
get { return flicker; }
|
||||
set
|
||||
{
|
||||
flicker = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(0.0f, true, description: "How rapidly the light blinks on and off (in Hz). 0 = no blinking.")]
|
||||
public float BlinkFrequency
|
||||
{
|
||||
get { return blinkFrequency; }
|
||||
set
|
||||
{
|
||||
blinkFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("255,255,255,255", true, description: "The color of the emitted light (R,G,B,A).")]
|
||||
public Color LightColor
|
||||
{
|
||||
get { return lightColor; }
|
||||
set
|
||||
{
|
||||
lightColor = value;
|
||||
#if CLIENT
|
||||
if (light != null) light.Color = IsActive ? lightColor : Color.Transparent;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
{
|
||||
#if CLIENT
|
||||
light.Position += amount;
|
||||
#endif
|
||||
}
|
||||
|
||||
public override bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.IsActive;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (base.IsActive == value) { return; }
|
||||
base.IsActive = value;
|
||||
|
||||
SetLightSourceState(value, value ? lightBrightness : 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public LightComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
#if CLIENT
|
||||
light = new LightSource(element)
|
||||
{
|
||||
ParentSub = item.CurrentHull?.Submarine,
|
||||
Position = item.Position,
|
||||
CastShadows = castShadows,
|
||||
IsBackground = drawBehindSubs,
|
||||
SpriteScale = Vector2.One * item.Scale,
|
||||
Range = range
|
||||
};
|
||||
#endif
|
||||
|
||||
IsActive = IsOn;
|
||||
item.AddTag("light");
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
itemLoaded = true;
|
||||
SetLightSourceState(IsActive, lightBrightness);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
UpdateAITarget(item.AiTarget);
|
||||
}
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
|
||||
#if CLIENT
|
||||
light.ParentSub = item.Submarine;
|
||||
#endif
|
||||
if (item.Container != null)
|
||||
{
|
||||
SetLightSourceState(false, 0.0f);
|
||||
return;
|
||||
}
|
||||
#if CLIENT
|
||||
light.Position = ParentBody != null ? ParentBody.Position : item.Position;
|
||||
#endif
|
||||
|
||||
PhysicsBody body = ParentBody ?? item.body;
|
||||
if (body != null)
|
||||
{
|
||||
#if CLIENT
|
||||
light.Rotation = body.Dir > 0.0f ? body.DrawRotation : body.DrawRotation - MathHelper.Pi;
|
||||
light.LightSpriteEffect = (body.Dir > 0.0f) ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
#endif
|
||||
if (!body.Enabled)
|
||||
{
|
||||
SetLightSourceState(false, 0.0f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if CLIENT
|
||||
light.Rotation = -Rotation;
|
||||
#endif
|
||||
}
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
if (Rand.Range(0.0f, 1.0f) < 0.05f && Voltage < Rand.Range(0.0f, MinVoltage))
|
||||
{
|
||||
#if CLIENT
|
||||
if (Voltage > 0.1f)
|
||||
{
|
||||
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
}
|
||||
#endif
|
||||
lightBrightness = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
lightBrightness = MathHelper.Lerp(lightBrightness, Math.Min(Voltage, 1.0f), 0.1f);
|
||||
}
|
||||
|
||||
if (blinkFrequency > 0.0f)
|
||||
{
|
||||
blinkTimer = (blinkTimer + deltaTime * blinkFrequency) % 1.0f;
|
||||
}
|
||||
|
||||
if (blinkTimer > 0.5f)
|
||||
{
|
||||
SetLightSourceState(false, lightBrightness);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLightSourceState(true, lightBrightness * (1.0f - Rand.Range(0.0f, flicker)));
|
||||
}
|
||||
|
||||
if (powerIn == null && powerConsumption > 0.0f) { Voltage -= deltaTime; }
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
SetLightSourceState(false, 0.0f);
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "toggle":
|
||||
IsActive = !IsActive;
|
||||
break;
|
||||
case "set_state":
|
||||
IsActive = (signal != "0");
|
||||
break;
|
||||
case "set_color":
|
||||
LightColor = XMLExtensions.ParseColor(signal, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(IsOn);
|
||||
}
|
||||
|
||||
private void UpdateAITarget(AITarget target)
|
||||
{
|
||||
target.Enabled = IsActive;
|
||||
if (!IsActive) { return; }
|
||||
if (target.MaxSightRange <= 0)
|
||||
{
|
||||
target.MaxSightRange = Range * 5;
|
||||
}
|
||||
target.SightRange = target.MaxSightRange * lightBrightness;
|
||||
}
|
||||
|
||||
partial void SetLightSourceState(bool enabled, float brightness);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class MemoryComponent : ItemComponent
|
||||
{
|
||||
[InGameEditable, Serialize("", true, description: "The currently stored signal the item outputs.")]
|
||||
public string Value
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
protected bool writeable = true;
|
||||
|
||||
public MemoryComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
item.SendSignal(0, Value, "signal_out", null);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_in":
|
||||
if (writeable) { Value = signal; }
|
||||
break;
|
||||
case "signal_store":
|
||||
writeable = (signal == "1");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class ModuloComponent : ItemComponent
|
||||
{
|
||||
private float modulus;
|
||||
[InGameEditable, Serialize(1.0f, false, description: "The modulus of the operation. Must be non-zero.")]
|
||||
public float Modulus
|
||||
{
|
||||
get { return modulus; }
|
||||
set
|
||||
{
|
||||
modulus = MathUtils.NearlyEqual(value, 0.0f) ? 1.0f : value;
|
||||
}
|
||||
}
|
||||
|
||||
public ModuloComponent(Item item, XElement element) : base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "set_modulus":
|
||||
case "modulus":
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float newModulus);
|
||||
Modulus = newModulus;
|
||||
break;
|
||||
case "signal_in":
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float value);
|
||||
item.SendSignal(0, (value % modulus).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class MotionSensor : ItemComponent
|
||||
{
|
||||
private const float UpdateInterval = 0.1f;
|
||||
private float rangeX, rangeY;
|
||||
|
||||
private Vector2 detectOffset;
|
||||
|
||||
private float updateTimer;
|
||||
|
||||
[Serialize(false, false, description: "Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
|
||||
public bool MotionDetected { get; set; }
|
||||
|
||||
[Editable, Serialize(false, true, description: "Should the sensor only detect the movement of humans?")]
|
||||
public bool OnlyHumans
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Should the sensor ignore the bodies of dead characters?")]
|
||||
public bool IgnoreDead
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
[InGameEditable, Serialize(0.0f, true, description: "Horizontal detection range.")]
|
||||
public float RangeX
|
||||
{
|
||||
get { return rangeX; }
|
||||
set
|
||||
{
|
||||
rangeX = MathHelper.Clamp(value, 0.0f, 1000.0f);
|
||||
}
|
||||
}
|
||||
[InGameEditable, Serialize(0.0f, true, description: "Vertical movement detection range.")]
|
||||
public float RangeY
|
||||
{
|
||||
get { return rangeY; }
|
||||
set
|
||||
{
|
||||
rangeY = MathHelper.Clamp(value, 0.0f, 1000.0f);
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize("0,0", true, description: "The position to detect the movement at relative to the item. For example, 0,100 would detect movement 100 units above the item.")]
|
||||
public Vector2 DetectOffset
|
||||
{
|
||||
get { return detectOffset; }
|
||||
set
|
||||
{
|
||||
detectOffset = value;
|
||||
detectOffset.X = MathHelper.Clamp(value.X, -rangeX, rangeX);
|
||||
detectOffset.Y = MathHelper.Clamp(value.Y, -rangeY, rangeY);
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected movement.")]
|
||||
public string Output { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The signal the item outputs when it has not detected movement.")]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
[Editable(DecimalCount = 3), Serialize(0.01f, true, description: "How fast the objects within the detector's range have to be moving (in m/s).")]
|
||||
public float MinimumVelocity
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public MotionSensor(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
//backwards compatibility
|
||||
if (element.Attribute("range") != null)
|
||||
{
|
||||
rangeX = rangeY = element.GetAttributeFloat("range", 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
string signalOut = MotionDetected ? Output : FalseOutput;
|
||||
|
||||
if (!string.IsNullOrEmpty(signalOut)) item.SendSignal(1, signalOut, "state_out", null);
|
||||
|
||||
updateTimer -= deltaTime;
|
||||
if (updateTimer > 0.0f) return;
|
||||
|
||||
MotionDetected = false;
|
||||
updateTimer = UpdateInterval;
|
||||
|
||||
if (item.body != null && item.body.Enabled)
|
||||
{
|
||||
if (Math.Abs(item.body.LinearVelocity.X) > MinimumVelocity || Math.Abs(item.body.LinearVelocity.Y) > MinimumVelocity)
|
||||
{
|
||||
MotionDetected = true;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 detectPos = item.WorldPosition + detectOffset;
|
||||
Rectangle detectRect = new Rectangle((int)(detectPos.X - rangeX), (int)(detectPos.Y - rangeY), (int)(rangeX * 2), (int)(rangeY * 2));
|
||||
float broadRangeX = Math.Max(rangeX * 2, 500);
|
||||
float broadRangeY = Math.Max(rangeY * 2, 500);
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (IgnoreDead && c.IsDead) { continue; }
|
||||
if (OnlyHumans && !c.IsHuman) { 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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (limb.LinearVelocity.LengthSquared() <= MinimumVelocity * MinimumVelocity) continue;
|
||||
if (MathUtils.CircleIntersectsRectangle(limb.WorldPosition, ConvertUnits.ToDisplayUnits(limb.body.GetMaxExtent()), detectRect))
|
||||
{
|
||||
MotionDetected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
detectOffset.X = -detectOffset.X;
|
||||
}
|
||||
public override void FlipY(bool relativeToSub)
|
||||
{
|
||||
detectOffset.Y = -detectOffset.Y;
|
||||
}
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
Vector2 prevDetectOffset = detectOffset;
|
||||
//undo flipping before saving
|
||||
if (item.FlippedX) { detectOffset.X = -detectOffset.X; }
|
||||
if (item.FlippedY) { detectOffset.Y = -detectOffset.Y; }
|
||||
XElement element = base.Save(parentElement);
|
||||
detectOffset = prevDetectOffset;
|
||||
return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class MultiplyComponent : ArithmeticComponent
|
||||
{
|
||||
public MultiplyComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
protected override float Calculate(float signal1, float signal2)
|
||||
{
|
||||
return signal1 * signal2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class NotComponent : ItemComponent
|
||||
{
|
||||
public NotComponent(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
if (connection.Name != "signal_in") return;
|
||||
|
||||
item.SendSignal(stepsTaken, signal == "0" ? "1" : "0", "signal_out", sender, 0.0f, source, signalStrength);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class OrComponent : AndComponent
|
||||
{
|
||||
public OrComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
bool sendOutput = false;
|
||||
for (int i = 0; i < timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] <= timeFrame) sendOutput = true;
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
string signalOut = sendOutput ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut)) return;
|
||||
|
||||
item.SendSignal(0, signalOut, "signal_out", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class OscillatorComponent : ItemComponent
|
||||
{
|
||||
public enum WaveType
|
||||
{
|
||||
Pulse,
|
||||
Sine,
|
||||
Square,
|
||||
}
|
||||
|
||||
private float frequency;
|
||||
|
||||
private float phase;
|
||||
|
||||
[InGameEditable, Serialize(WaveType.Pulse, true, description: "What kind of a signal the item outputs." +
|
||||
" Pulse: periodically sends out a signal of 1." +
|
||||
" Sine: sends out a sine wave oscillating between -1 and 1." +
|
||||
" Square: sends out a signal that alternates between 0 and 1.")]
|
||||
public WaveType OutputType
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[InGameEditable(DecimalCount = 2), Serialize(1.0f, true, description: "How fast the signal oscillates, or how fast the pulses are sent (in Hz).")]
|
||||
public float Frequency
|
||||
{
|
||||
get { return frequency; }
|
||||
set
|
||||
{
|
||||
//capped to 240 Hz (= 4 signals per frame) to prevent players
|
||||
//from wrecking the performance by setting the value too high
|
||||
frequency = MathHelper.Clamp(value, 0.0f, 240.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public OscillatorComponent(Item item, XElement element) :
|
||||
base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
switch (OutputType)
|
||||
{
|
||||
case WaveType.Pulse:
|
||||
if (frequency <= 0.0f) return;
|
||||
|
||||
phase += deltaTime;
|
||||
float pulseInterval = 1.0f / frequency;
|
||||
while (phase >= pulseInterval)
|
||||
{
|
||||
item.SendSignal(0, "1", "signal_out", null);
|
||||
phase -= pulseInterval;
|
||||
}
|
||||
break;
|
||||
case WaveType.Square:
|
||||
phase = (phase + deltaTime * frequency) % 1.0f;
|
||||
item.SendSignal(0, phase < 0.5f ? "0" : "1", "signal_out", null);
|
||||
break;
|
||||
case WaveType.Sine:
|
||||
phase = (phase + deltaTime * frequency) % 1.0f;
|
||||
item.SendSignal(0, Math.Sin(phase * MathHelper.TwoPi).ToString(CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "set_frequency":
|
||||
case "frequency_in":
|
||||
float newFrequency;
|
||||
if (float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out newFrequency))
|
||||
{
|
||||
Frequency = newFrequency;
|
||||
}
|
||||
IsActive = true;
|
||||
break;
|
||||
case "set_outputtype":
|
||||
case "set_wavetype":
|
||||
WaveType newOutputType;
|
||||
if (Enum.TryParse(signal, out newOutputType))
|
||||
{
|
||||
OutputType = newOutputType;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class OxygenDetector : ItemComponent
|
||||
{
|
||||
public OxygenDetector(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (item.CurrentHull == null) return;
|
||||
|
||||
item.SendSignal(0, ((int)item.CurrentHull.OxygenPercentage).ToString(), "signal_out", null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class RegExFindComponent : ItemComponent
|
||||
{
|
||||
private string expression;
|
||||
|
||||
private string receivedSignal;
|
||||
private string previousReceivedSignal;
|
||||
|
||||
private bool previousResult;
|
||||
|
||||
private Regex regex;
|
||||
|
||||
private bool nonContinuousOutputSent;
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the regular expression.")]
|
||||
public string Output { get; set; }
|
||||
|
||||
[Serialize("0", true, description: "The signal this item outputs when the received signal does not match the regular expression.")]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
[InGameEditable, Serialize(true, true, description: "Should the component keep sending the output even after it stops receiving a signal, or only send an output when it receives a signal.")]
|
||||
public bool ContinuousOutput { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The regular expression used to check the incoming signals.")]
|
||||
public string Expression
|
||||
{
|
||||
get { return expression; }
|
||||
set
|
||||
{
|
||||
if (expression == value) return;
|
||||
expression = value;
|
||||
previousReceivedSignal = "";
|
||||
|
||||
try
|
||||
{
|
||||
regex = new Regex(@expression);
|
||||
}
|
||||
|
||||
catch
|
||||
{
|
||||
item.SendSignal(0, "ERROR", "signal_out", null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RegExFindComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(expression) || regex == null) return;
|
||||
|
||||
if (receivedSignal != previousReceivedSignal && receivedSignal != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Match match = regex.Match(receivedSignal);
|
||||
previousResult = match.Success;
|
||||
previousReceivedSignal = receivedSignal;
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
item.SendSignal(0, "ERROR", "signal_out", null);
|
||||
previousResult = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string signalOut = previousResult ? Output : FalseOutput;
|
||||
if (ContinuousOutput)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(signalOut)) { item.SendSignal(0, signalOut, "signal_out", null); }
|
||||
}
|
||||
else if (!nonContinuousOutputSent)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(signalOut)) { item.SendSignal(0, signalOut, "signal_out", null); }
|
||||
nonContinuousOutputSent = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_in":
|
||||
receivedSignal = signal;
|
||||
nonContinuousOutputSent = false;
|
||||
break;
|
||||
case "set_output":
|
||||
Output = signal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class RelayComponent : PowerTransfer, IServerSerializable
|
||||
{
|
||||
private float maxPower;
|
||||
|
||||
private bool isOn;
|
||||
|
||||
private float throttlePowerOutput;
|
||||
|
||||
private static readonly Dictionary<string, string> connectionPairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "power_in", "power_out"},
|
||||
{ "signal_in", "signal_out" },
|
||||
{ "signal_in1", "signal_out1" },
|
||||
{ "signal_in2", "signal_out2" },
|
||||
{ "signal_in3", "signal_out3" },
|
||||
{ "signal_in4", "signal_out4" },
|
||||
{ "signal_in5", "signal_out5" }
|
||||
};
|
||||
public float DisplayLoad { get; set; }
|
||||
|
||||
[Editable, Serialize(1000.0f, true, description: "The maximum amount of power that can pass through the item.")]
|
||||
public float MaxPower
|
||||
{
|
||||
get { return maxPower; }
|
||||
set
|
||||
{
|
||||
maxPower = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Can the relay currently pass power and signals through it.")]
|
||||
public bool IsOn
|
||||
{
|
||||
get
|
||||
{
|
||||
return isOn;
|
||||
}
|
||||
set
|
||||
{
|
||||
isOn = value;
|
||||
CanTransfer = value;
|
||||
if (!isOn)
|
||||
{
|
||||
currPowerConsumption = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RelayComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
throttlePowerOutput = MaxPower;
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
var connections = Item.Connections;
|
||||
if (connections != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> connectionPair in connectionPairs)
|
||||
{
|
||||
if (connections.Any(c => c.Name == connectionPair.Key) && !connections.Any(c => c.Name == connectionPair.Value))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item \"" + Name + "\" - matching connection pair not found for the connection \"" + connectionPair.Key + "\" (expecting \"" + connectionPair.Value + "\").");
|
||||
}
|
||||
else if (connections.Any(c => c.Name == connectionPair.Value) && !connections.Any(c => c.Name == connectionPair.Key))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item \"" + Name + "\" - matching connection pair not found for the connection \"" + connectionPair.Value + "\" (expecting \"" + connectionPair.Key + "\").");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
RefreshConnections();
|
||||
|
||||
item.SendSignal(0, IsOn ? "1" : "0", "state_out", null);
|
||||
|
||||
if (!CanTransfer) { Voltage = 0.0f; return; }
|
||||
|
||||
if (isBroken)
|
||||
{
|
||||
SetAllConnectionsDirty();
|
||||
isBroken = false;
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
if (powerOut != null)
|
||||
{
|
||||
bool overloaded = false;
|
||||
foreach (Connection recipient in powerOut.Recipients)
|
||||
{
|
||||
var pt = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null)
|
||||
{
|
||||
float overload = -pt.CurrPowerConsumption - pt.PowerLoad;
|
||||
throttlePowerOutput += overload * deltaTime * 0.5f;
|
||||
overloaded = overload > 1.0f;
|
||||
}
|
||||
}
|
||||
throttlePowerOutput = overloaded ?
|
||||
MathHelper.Clamp(throttlePowerOutput, 0.0f, MaxPower):
|
||||
Math.Max(throttlePowerOutput - MaxPower * 0.1f * deltaTime, 0.0f);
|
||||
}
|
||||
|
||||
if (Math.Min(-currPowerConsumption, PowerLoad) > maxPower && CanBeOverloaded)
|
||||
{
|
||||
item.Condition = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceivePowerProbeSignal(Connection connection, Item source, float power)
|
||||
{
|
||||
if (!IsOn || item.Condition <= 0.0f) { return; }
|
||||
|
||||
//we've already received this signal
|
||||
if (lastPowerProbeRecipients.Contains(this)) { return; }
|
||||
lastPowerProbeRecipients.Add(this);
|
||||
|
||||
if (power < 0.0f)
|
||||
{
|
||||
if (!connection.IsOutput || powerIn == null) { return; }
|
||||
|
||||
//power being drawn from the power_out connection
|
||||
DisplayLoad -= Math.Min(power, 0.0f);
|
||||
powerLoad -= Math.Min(power + throttlePowerOutput, 0.0f);
|
||||
|
||||
//pass the load to items connected to the input
|
||||
powerIn.SendPowerProbeSignal(source, Math.Max(power, -MaxPower));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (connection.IsOutput || powerOut == null) { return; }
|
||||
//power being supplied to the power_in connection
|
||||
if (currPowerConsumption - power < -MaxPower)
|
||||
{
|
||||
power += MaxPower + (currPowerConsumption - power);
|
||||
}
|
||||
|
||||
currPowerConsumption -= power;
|
||||
|
||||
foreach (Connection recipient in powerOut.Recipients)
|
||||
{
|
||||
if (!recipient.IsPower) { continue; }
|
||||
var powered = recipient.Item.GetComponent<Powered>();
|
||||
if (powered == null) { continue; }
|
||||
|
||||
float load = powered.CurrPowerConsumption;
|
||||
var powerTransfer = powered as PowerTransfer;
|
||||
if (powerTransfer != null) { load = powerTransfer.PowerLoad; }
|
||||
|
||||
float powerOut = power * (load / Math.Max(powerLoad + throttlePowerOutput, 0.01f));
|
||||
powered.ReceivePowerProbeSignal(recipient, source, Math.Min(powerOut, power));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
if (item.Condition <= 0.0f || connection.IsPower) { return; }
|
||||
|
||||
if (connectionPairs.TryGetValue(connection.Name, out string outConnection))
|
||||
{
|
||||
if (!IsOn) { return; }
|
||||
item.SendSignal(stepsTaken, signal, outConnection, sender, power, source, signalStrength);
|
||||
}
|
||||
else if (connection.Name == "toggle")
|
||||
{
|
||||
SetState(!IsOn, false);
|
||||
}
|
||||
else if (connection.Name == "set_state")
|
||||
{
|
||||
SetState(signal != "0", false);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetState(bool on, bool isNetworkMessage)
|
||||
{
|
||||
#if CLIENT
|
||||
if (GameMain.Client != null && !isNetworkMessage) return;
|
||||
#endif
|
||||
|
||||
#if SERVER
|
||||
if (on != IsOn && GameMain.Server != null)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
IsOn = on;
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(isOn);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
SetState(msg.ReadBoolean(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class SignalCheckComponent : ItemComponent
|
||||
{
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the target signal.")]
|
||||
public string Output { get; set; }
|
||||
[InGameEditable, Serialize("0", true, description: "The signal this item outputs when the received signal does not match the target signal.")]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The value to compare the received signals against.")]
|
||||
public string TargetSignal { get; set; }
|
||||
|
||||
public SignalCheckComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_in":
|
||||
string signalOut = (signal == TargetSignal) ? Output : FalseOutput;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(signalOut)) return;
|
||||
item.SendSignal(stepsTaken, signalOut, "signal_out", sender, signalStrength);
|
||||
|
||||
break;
|
||||
case "set_output":
|
||||
Output = signal;
|
||||
break;
|
||||
case "set_targetsignal":
|
||||
TargetSignal = signal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class SmokeDetector : ItemComponent
|
||||
{
|
||||
[Serialize(50.0f, false, description: "How large the fire has to be for the detector to react to it.")]
|
||||
public float FireSizeThreshold
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public SmokeDetector(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
item.SendSignal(0, item.CurrentHull != null && item.CurrentHull.FireSources.Any(fs => fs.Size.X > FireSizeThreshold) ? "1" : "0", "signal_out", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class SubtractComponent : ArithmeticComponent
|
||||
{
|
||||
public SubtractComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
protected override float Calculate(float signal1, float signal2)
|
||||
{
|
||||
return signal1 - signal2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Terminal : ItemComponent
|
||||
{
|
||||
private const int MaxMessageLength = 150;
|
||||
|
||||
public string DisplayedWelcomeMessage
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private string welcomeMessage;
|
||||
[InGameEditable, Serialize("", true, "Message to be displayed on the terminal display when it is first opened.", translationTextTag = "terminalwelcomemsg.")]
|
||||
public string WelcomeMessage
|
||||
{
|
||||
get { return welcomeMessage; }
|
||||
set
|
||||
{
|
||||
if (welcomeMessage == value) { return; }
|
||||
welcomeMessage = value;
|
||||
DisplayedWelcomeMessage = TextManager.Get(welcomeMessage, returnNull: true) ?? welcomeMessage;
|
||||
}
|
||||
}
|
||||
|
||||
private string OutputValue { get; set; }
|
||||
|
||||
public Terminal(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
|
||||
partial void ShowOnDisplay(string input);
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
if (connection.Name != "signal_in") { return; }
|
||||
if (signal.Length > MaxMessageLength)
|
||||
{
|
||||
signal = signal.Substring(0, MaxMessageLength);
|
||||
}
|
||||
ShowOnDisplay(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class TrigonometricFunctionComponent : ItemComponent
|
||||
{
|
||||
public enum FunctionType
|
||||
{
|
||||
Sin,
|
||||
Cos,
|
||||
Tan,
|
||||
Asin,
|
||||
Acos,
|
||||
Atan,
|
||||
}
|
||||
|
||||
protected float[] receivedSignal = new float[2];
|
||||
|
||||
[Serialize(FunctionType.Sin, false, description: "Which kind of function to run the input through.")]
|
||||
public FunctionType Function
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "If set to true, the trigonometric function uses radians instead of degrees.")]
|
||||
public bool UseRadians
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
public TrigonometricFunctionComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
//reset received signals
|
||||
receivedSignal[0] = float.NaN;
|
||||
receivedSignal[1] = float.NaN;
|
||||
}
|
||||
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float value);
|
||||
switch (Function)
|
||||
{
|
||||
case FunctionType.Sin:
|
||||
if (!UseRadians) { value = MathHelper.ToRadians(value); }
|
||||
item.SendSignal(0, ((float)Math.Sin(value)).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Cos:
|
||||
if (!UseRadians) { value = MathHelper.ToRadians(value); }
|
||||
item.SendSignal(0, ((float)Math.Cos(value)).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Tan:
|
||||
if (!UseRadians) { value = MathHelper.ToRadians(value); }
|
||||
item.SendSignal(0, ((float)Math.Tan(value)).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Asin:
|
||||
{
|
||||
float angle = (float)Math.Asin(value);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(0, angle.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
break;
|
||||
case FunctionType.Acos:
|
||||
{
|
||||
float angle = (float)Math.Acos(value);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(0, angle.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
break;
|
||||
case FunctionType.Atan:
|
||||
if (connection.Name == "signal_in_x")
|
||||
{
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[0]);
|
||||
}
|
||||
else if (connection.Name == "signal_in_y")
|
||||
{
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[1]);
|
||||
if (!float.IsNaN(receivedSignal[0]) && !float.IsNaN(receivedSignal[1]))
|
||||
{
|
||||
float angle = (float)Math.Atan2(receivedSignal[1], receivedSignal[0]);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(0, angle.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float angle = (float)Math.Atan(value);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(0, angle.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Function {Function} has not been implemented.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class WaterDetector : ItemComponent
|
||||
{
|
||||
//how often the detector can switch from state to another
|
||||
const float StateSwitchInterval = 1.0f;
|
||||
|
||||
private bool isInWater;
|
||||
private float stateSwitchDelay;
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal the item sends out when it's underwater.")]
|
||||
public string Output { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("0", true, description: "The signal the item sends out when it's not underwater.")]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
public WaterDetector(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (stateSwitchDelay > 0.0f)
|
||||
{
|
||||
stateSwitchDelay -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool prevState = isInWater;
|
||||
|
||||
isInWater = false;
|
||||
if (item.InWater)
|
||||
{
|
||||
//item in water -> we definitely want to send the True output
|
||||
isInWater = true;
|
||||
}
|
||||
else if (item.CurrentHull != null)
|
||||
{
|
||||
//item in not water -> check if there's water anywhere within the rect of the item
|
||||
if (item.CurrentHull.Surface > item.CurrentHull.Rect.Y - item.CurrentHull.Rect.Height + 1 &&
|
||||
item.CurrentHull.Surface > item.Rect.Y - item.Rect.Height)
|
||||
{
|
||||
isInWater = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (prevState != isInWater)
|
||||
{
|
||||
stateSwitchDelay = StateSwitchInterval;
|
||||
}
|
||||
}
|
||||
|
||||
string signalOut = isInWater ? Output : FalseOutput;
|
||||
if (!string.IsNullOrEmpty(signalOut))
|
||||
{
|
||||
item.SendSignal(0, signalOut, "signal_out", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class WifiComponent : ItemComponent
|
||||
{
|
||||
private static List<WifiComponent> list = new List<WifiComponent>();
|
||||
|
||||
private float range;
|
||||
|
||||
private int channel;
|
||||
|
||||
private float chatMsgCooldown;
|
||||
|
||||
private string prevSignal;
|
||||
|
||||
[Serialize(Character.TeamType.None, false, description: "WiFi components can only communicate with components that have the same Team ID.")]
|
||||
public Character.TeamType TeamID { get; set; }
|
||||
|
||||
[Editable, Serialize(20000.0f, false, description: "How close the recipient has to be to receive a signal from this WiFi component.")]
|
||||
public float Range
|
||||
{
|
||||
get { return range; }
|
||||
set { range = Math.Max(value, 0.0f); }
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(1, true, description: "WiFi components can only communicate with components that use the same channel.")]
|
||||
public int Channel
|
||||
{
|
||||
get { return channel; }
|
||||
set
|
||||
{
|
||||
channel = MathHelper.Clamp(value, 0, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "If enabled, any signals received from another chat-linked wifi component are displayed " +
|
||||
"as chat messages in the chatbox of the player holding the item.")]
|
||||
public bool LinkToChat
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(1.0f, true, description: "How many seconds have to pass between signals for a message to be displayed in the chatbox. " +
|
||||
"Setting this to a very low value is not recommended, because it may cause an excessive amount of chat messages to be created " +
|
||||
"if there are chat-linked wifi components that transmit a continuous signal.")]
|
||||
public float MinChatMessageInterval
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "If set to true, the component will only create chat messages when the received signal changes.")]
|
||||
public bool DiscardDuplicateChatMessages
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public WifiComponent(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
list.Add(this);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public bool CanTransmit()
|
||||
{
|
||||
return HasRequiredContainedItems(user: null, addMessage: false);
|
||||
}
|
||||
|
||||
public IEnumerable<WifiComponent> GetReceiversInRange()
|
||||
{
|
||||
return list.Where(w => w != this && w.CanReceive(this));
|
||||
}
|
||||
|
||||
public bool CanReceive(WifiComponent sender)
|
||||
{
|
||||
if (sender == null || sender.channel != channel) { return false; }
|
||||
if (sender.TeamID == Character.TeamType.Team1 && TeamID == Character.TeamType.Team2) { return false; }
|
||||
if (sender.TeamID == Character.TeamType.Team2 && TeamID == Character.TeamType.Team1) { return false; }
|
||||
|
||||
if (Vector2.DistanceSquared(item.WorldPosition, sender.item.WorldPosition) > sender.range * sender.range) { return false; }
|
||||
|
||||
return HasRequiredContainedItems(user: null, addMessage: false);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
chatMsgCooldown -= deltaTime;
|
||||
}
|
||||
|
||||
public void TransmitSignal(int stepsTaken, string signal, Item source, Character sender, bool sendToChat, float signalStrength = 1.0f)
|
||||
{
|
||||
var senderComponent = source?.GetComponent<WifiComponent>();
|
||||
if (senderComponent != null && !CanReceive(senderComponent)) return;
|
||||
|
||||
bool chatMsgSent = false;
|
||||
|
||||
var receivers = GetReceiversInRange();
|
||||
foreach (WifiComponent wifiComp in receivers)
|
||||
{
|
||||
//signal strength diminishes by distance
|
||||
float sentSignalStrength = signalStrength *
|
||||
MathHelper.Clamp(1.0f - (Vector2.Distance(item.WorldPosition, wifiComp.item.WorldPosition) / wifiComp.range), 0.0f, 1.0f);
|
||||
wifiComp.item.SendSignal(stepsTaken, signal, "signal_out", sender, 0, source, sentSignalStrength);
|
||||
|
||||
if (source != null)
|
||||
{
|
||||
foreach (Item receiverItem in wifiComp.item.LastSentSignalRecipients)
|
||||
{
|
||||
if (!source.LastSentSignalRecipients.Contains(receiverItem))
|
||||
{
|
||||
source.LastSentSignalRecipients.Add(receiverItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DiscardDuplicateChatMessages && signal == prevSignal) continue;
|
||||
|
||||
if (LinkToChat && wifiComp.LinkToChat && chatMsgCooldown <= 0.0f && sendToChat)
|
||||
{
|
||||
if (wifiComp.item.ParentInventory != null &&
|
||||
wifiComp.item.ParentInventory.Owner != null &&
|
||||
GameMain.NetworkMember != null)
|
||||
{
|
||||
string chatMsg = signal;
|
||||
if (senderComponent != null)
|
||||
{
|
||||
chatMsg = ChatMessage.ApplyDistanceEffect(chatMsg, 1.0f - sentSignalStrength);
|
||||
}
|
||||
if (chatMsg.Length > ChatMessage.MaxLength) chatMsg = chatMsg.Substring(0, ChatMessage.MaxLength);
|
||||
if (string.IsNullOrEmpty(chatMsg)) continue;
|
||||
|
||||
#if CLIENT
|
||||
if (wifiComp.item.ParentInventory.Owner == Character.Controlled)
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
GameMain.NetworkMember.AddChatMessage(signal, ChatMessageType.Radio, source == null ? "" : source.Name);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
Client recipientClient = GameMain.Server.ConnectedClients.Find(c => c.Character == wifiComp.item.ParentInventory.Owner);
|
||||
if (recipientClient != null)
|
||||
{
|
||||
GameMain.Server.SendDirectChatMessage(
|
||||
ChatMessage.Create(source == null ? "" : source.Name, chatMsg, ChatMessageType.Radio, null), recipientClient);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
chatMsgSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chatMsgSent) chatMsgCooldown = MinChatMessageInterval;
|
||||
|
||||
prevSignal = signal;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
if (connection == null || connection.Name != "signal_in") return;
|
||||
TransmitSignal(stepsTaken, signal, source, sender, true, signalStrength);
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
list.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,754 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Wire : ItemComponent, IDrawableComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
partial class WireSection
|
||||
{
|
||||
private Vector2 start;
|
||||
private Vector2 end;
|
||||
|
||||
private readonly float angle;
|
||||
private readonly float length;
|
||||
|
||||
public Vector2 Start
|
||||
{
|
||||
get { return start; }
|
||||
}
|
||||
public Vector2 End
|
||||
{
|
||||
get { return end; }
|
||||
}
|
||||
|
||||
public WireSection(Vector2 start, Vector2 end)
|
||||
{
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
|
||||
angle = MathUtils.VectorToAngle(end - start);
|
||||
length = Vector2.Distance(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
const float MaxAttachDistance = 150.0f;
|
||||
|
||||
const float MinNodeDistance = 15.0f;
|
||||
|
||||
const int MaxNodeCount = 255;
|
||||
const int MaxNodesPerNetworkEvent = 30;
|
||||
|
||||
private List<Vector2> nodes;
|
||||
private readonly List<WireSection> sections;
|
||||
|
||||
private Connection[] connections;
|
||||
|
||||
private bool canPlaceNode;
|
||||
private Vector2 newNodePos;
|
||||
|
||||
private Vector2 sectionExtents;
|
||||
|
||||
public bool Hidden;
|
||||
|
||||
private float removeNodeDelay;
|
||||
|
||||
private bool locked;
|
||||
public bool Locked
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GameMain.NetworkMember?.ServerSettings != null && !GameMain.NetworkMember.ServerSettings.AllowRewiring) { return false; }
|
||||
return locked || connections.Any(c => c != null && c.ConnectionPanel.Locked);
|
||||
}
|
||||
set { locked = value; }
|
||||
}
|
||||
|
||||
public Connection[] Connections
|
||||
{
|
||||
get { return connections; }
|
||||
}
|
||||
|
||||
[Serialize(5000.0f, false, description: "The maximum distance the wire can extend (in pixels).")]
|
||||
public float MaxLength
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Wire(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
nodes = new List<Vector2>();
|
||||
sections = new List<WireSection>();
|
||||
connections = new Connection[2];
|
||||
IsActive = false;
|
||||
item.IsShootable = true;
|
||||
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
|
||||
public Connection OtherConnection(Connection connection)
|
||||
{
|
||||
if (connection == connections[0]) { return connections[1]; }
|
||||
if (connection == connections[1]) { return connections[0]; }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsConnectedTo(Item item)
|
||||
{
|
||||
if (connections[0] != null && connections[0].Item == item) return true;
|
||||
return (connections[1] != null && connections[1].Item == item);
|
||||
}
|
||||
|
||||
public void RemoveConnection(Item item)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i] == null || connections[i].Item != item) continue;
|
||||
|
||||
foreach (Wire wire in connections[i].Wires)
|
||||
{
|
||||
if (wire != this) continue;
|
||||
SetConnectedDirty();
|
||||
|
||||
connections[i].SetWire(connections[i].FindWireIndex(wire), null);
|
||||
}
|
||||
|
||||
connections[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveConnection(Connection connection)
|
||||
{
|
||||
if (connection == connections[0]) { connections[0] = null; }
|
||||
if (connection == connections[1]) { connections[1] = null; }
|
||||
|
||||
SetConnectedDirty();
|
||||
}
|
||||
|
||||
public bool Connect(Connection newConnection, bool addNode = true, bool sendNetworkEvent = false)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i] == newConnection) { return false; }
|
||||
}
|
||||
|
||||
if (!connections.Any(c => c == null)) { return false; }
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i] != null && connections[i].Item == newConnection.Item)
|
||||
{
|
||||
addNode = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (item.body != null) { item.Submarine = newConnection.Item.Submarine; }
|
||||
|
||||
newConnection.ConnectionPanel.DisconnectedWires.Remove(this);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i] != null) { continue; }
|
||||
|
||||
connections[i] = newConnection;
|
||||
FixNodeEnds();
|
||||
|
||||
if (!addNode) { break; }
|
||||
|
||||
Submarine refSub = newConnection.Item.Submarine;
|
||||
if (refSub == null)
|
||||
{
|
||||
Structure attachTarget = Structure.GetAttachTarget(newConnection.Item.WorldPosition);
|
||||
if (attachTarget == null) { continue; }
|
||||
refSub = attachTarget.Submarine;
|
||||
}
|
||||
|
||||
Vector2 nodePos = refSub == null ?
|
||||
newConnection.Item.Position :
|
||||
newConnection.Item.Position - refSub.HiddenSubPosition;
|
||||
|
||||
if (nodes.Count > 0 && nodes[0] == nodePos) { break; }
|
||||
if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) { break; }
|
||||
|
||||
//make sure we place the node at the correct end of the wire (the end that's closest to the new node pos)
|
||||
int newNodeIndex = 0;
|
||||
if (nodes.Count > 1)
|
||||
{
|
||||
if (Vector2.DistanceSquared(nodes[nodes.Count - 1], nodePos) < Vector2.DistanceSquared(nodes[0], nodePos))
|
||||
{
|
||||
newNodeIndex = nodes.Count;
|
||||
}
|
||||
}
|
||||
|
||||
if (newNodeIndex == 0)
|
||||
{
|
||||
nodes.Insert(0, nodePos);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodes.Add(nodePos);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
SetConnectedDirty();
|
||||
|
||||
if (connections[0] != null && connections[1] != null)
|
||||
{
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic == this) continue;
|
||||
ic.Drop(null);
|
||||
}
|
||||
if (item.Container != null) item.Container.RemoveContained(this.item);
|
||||
if (item.body != null) item.body.Enabled = false;
|
||||
|
||||
IsActive = false;
|
||||
|
||||
CleanNodes();
|
||||
}
|
||||
|
||||
if (item.body != null) item.Submarine = newConnection.Item.Submarine;
|
||||
|
||||
if (sendNetworkEvent)
|
||||
{
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
CreateNetworkEvent();
|
||||
}
|
||||
#endif
|
||||
//the wire is active if only one end has been connected
|
||||
IsActive = connections[0] == null ^ connections[1] == null;
|
||||
}
|
||||
|
||||
Drawable = IsActive || nodes.Any();
|
||||
|
||||
UpdateSections();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Equip(Character character)
|
||||
{
|
||||
ClearConnections(character);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Unequip(Character character)
|
||||
{
|
||||
ClearConnections(character);
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
{
|
||||
ClearConnections(dropper);
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (nodes.Count == 0) { return; }
|
||||
|
||||
Character user = item.ParentInventory?.Owner as Character;
|
||||
removeNodeDelay = (user?.SelectedConstruction == null) ? removeNodeDelay - deltaTime : 0.5f;
|
||||
|
||||
Submarine sub = null;
|
||||
if (connections[0] != null && connections[0].Item.Submarine != null) { sub = connections[0].Item.Submarine; }
|
||||
if (connections[1] != null && connections[1].Item.Submarine != null) { sub = connections[1].Item.Submarine; }
|
||||
|
||||
if (Screen.Selected != GameMain.SubEditorScreen)
|
||||
{
|
||||
//cannot run wires from sub to another
|
||||
if (item.Submarine != sub && sub != null && item.Submarine != null)
|
||||
{
|
||||
ClearConnections();
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.CurrentHull == null)
|
||||
{
|
||||
Structure attachTarget = Structure.GetAttachTarget(item.WorldPosition);
|
||||
canPlaceNode = attachTarget != null;
|
||||
|
||||
sub = sub ?? attachTarget?.Submarine;
|
||||
Vector2 attachPos = GetAttachPosition(user);
|
||||
newNodePos = sub == null ?
|
||||
attachPos :
|
||||
attachPos - sub.Position - sub.HiddenSubPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
newNodePos = GetAttachPosition(user);
|
||||
if (sub != null) { newNodePos -= sub.HiddenSubPosition; }
|
||||
canPlaceNode = true;
|
||||
}
|
||||
|
||||
//prevent the wire from extending too far when rewiring
|
||||
if (nodes.Count > 0)
|
||||
{
|
||||
if (user == null) { return; }
|
||||
|
||||
Vector2 prevNodePos = nodes[nodes.Count - 1];
|
||||
if (sub != null) { prevNodePos += sub.HiddenSubPosition; }
|
||||
|
||||
float currLength = 0.0f;
|
||||
for (int i = 0; i < nodes.Count - 1; i++)
|
||||
{
|
||||
currLength += Vector2.Distance(nodes[i], nodes[i + 1]);
|
||||
}
|
||||
currLength += Vector2.Distance(nodes[nodes.Count - 1], newNodePos);
|
||||
|
||||
if (currLength > MaxLength)
|
||||
{
|
||||
Vector2 diff = nodes[nodes.Count - 1] - newNodePos;
|
||||
Vector2 pullBackDir = diff == Vector2.Zero ? Vector2.Zero : Vector2.Normalize(diff);
|
||||
|
||||
user.AnimController.Collider.ApplyForce(pullBackDir * user.Mass * 50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
user.AnimController.UpdateUseItem(true, user.WorldPosition + pullBackDir * 200.0f);
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
if (currLength > MaxLength * 1.5f)
|
||||
{
|
||||
ClearConnections();
|
||||
#if SERVER
|
||||
CreateNetworkEvent();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newNodePos = RoundNode(item.Position);
|
||||
if (sub != null) { newNodePos -= sub.HiddenSubPosition; }
|
||||
canPlaceNode = true;
|
||||
}
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
Vector2 relativeNodePos = newNodePos - item.Position;
|
||||
|
||||
if (sub != null)
|
||||
{
|
||||
relativeNodePos += sub.HiddenSubPosition;
|
||||
}
|
||||
|
||||
sectionExtents = new Vector2(
|
||||
Math.Max(Math.Abs(relativeNodePos.X), sectionExtents.X),
|
||||
Math.Max(Math.Abs(relativeNodePos.Y), sectionExtents.Y));
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 GetAttachPosition(Character user)
|
||||
{
|
||||
if (user == null) { return item.Position; }
|
||||
|
||||
Vector2 mouseDiff = user.CursorWorldPosition - user.WorldPosition;
|
||||
mouseDiff = mouseDiff.ClampLength(MaxAttachDistance);
|
||||
|
||||
return new Vector2(
|
||||
MathUtils.RoundTowardsClosest(user.Position.X + mouseDiff.X, Submarine.GridSize.X),
|
||||
MathUtils.RoundTowardsClosest(user.Position.Y + mouseDiff.Y, Submarine.GridSize.Y));
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || character != Character.Controlled) { return false; }
|
||||
if (character.SelectedConstruction != null) { return false; }
|
||||
#if CLIENT
|
||||
if (Screen.Selected == GameMain.SubEditorScreen && !PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
//clients communicate node addition/removal with network events
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { return false; }
|
||||
if (newNodePos != Vector2.Zero && canPlaceNode && nodes.Count > 0 && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > MinNodeDistance)
|
||||
{
|
||||
if (nodes.Count >= MaxNodeCount)
|
||||
{
|
||||
nodes.RemoveAt(nodes.Count - 1);
|
||||
}
|
||||
|
||||
nodes.Add(newNodePos);
|
||||
CleanNodes();
|
||||
UpdateSections();
|
||||
Drawable = true;
|
||||
newNodePos = Vector2.Zero;
|
||||
#if CLIENT
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(item, new object[]
|
||||
{
|
||||
NetEntityEvent.Type.ComponentState,
|
||||
item.GetComponentIndex(this),
|
||||
nodes.Count
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || character != Character.Controlled) { return false; }
|
||||
|
||||
//clients communicate node addition/removal with network events
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { return false; }
|
||||
|
||||
if (nodes.Count > 1 && removeNodeDelay <= 0.0f)
|
||||
{
|
||||
nodes.RemoveAt(nodes.Count - 1);
|
||||
UpdateSections();
|
||||
#if CLIENT
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(item, new object[]
|
||||
{
|
||||
NetEntityEvent.Type.ComponentState,
|
||||
item.GetComponentIndex(this),
|
||||
nodes.Count
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
removeNodeDelay = 0.1f;
|
||||
|
||||
Drawable = IsActive || sections.Count > 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
ClearConnections(picker);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
{
|
||||
#if CLIENT
|
||||
if (item.IsSelected) MoveNodes(amount);
|
||||
#endif
|
||||
}
|
||||
|
||||
public List<Vector2> GetNodes()
|
||||
{
|
||||
return new List<Vector2>(nodes);
|
||||
}
|
||||
|
||||
public void SetNodes(List<Vector2> nodes)
|
||||
{
|
||||
this.nodes = new List<Vector2>(nodes);
|
||||
UpdateSections();
|
||||
}
|
||||
|
||||
public void MoveNode(int index, Vector2 amount)
|
||||
{
|
||||
if (index < 0 || index >= nodes.Count) return;
|
||||
nodes[index] += amount;
|
||||
UpdateSections();
|
||||
}
|
||||
|
||||
public void MoveNodes(Vector2 amount)
|
||||
{
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
nodes[i] += amount;
|
||||
}
|
||||
UpdateSections();
|
||||
}
|
||||
|
||||
public void UpdateSections()
|
||||
{
|
||||
sections.Clear();
|
||||
|
||||
for (int i = 0; i < nodes.Count - 1; i++)
|
||||
{
|
||||
sections.Add(new WireSection(nodes[i], nodes[i + 1]));
|
||||
}
|
||||
Drawable = IsActive || sections.Count > 0;
|
||||
CalculateExtents();
|
||||
}
|
||||
|
||||
private void CalculateExtents()
|
||||
{
|
||||
sectionExtents = Vector2.Zero;
|
||||
if (sections.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
sectionExtents.X = Math.Max(Math.Abs(nodes[i].X - item.Position.X), sectionExtents.X);
|
||||
sectionExtents.Y = Math.Max(Math.Abs(nodes[i].Y - item.Position.Y), sectionExtents.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearConnections(Character user = null)
|
||||
{
|
||||
nodes.Clear();
|
||||
sections.Clear();
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
var connectionPanel = item.GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel != null && connectionPanel.DisconnectedWires.Contains(this))
|
||||
{
|
||||
#if SERVER
|
||||
item.CreateServerEvent(connectionPanel);
|
||||
#endif
|
||||
connectionPanel.DisconnectedWires.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
if (user != null)
|
||||
{
|
||||
if (connections[0] != null || connections[1] != null)
|
||||
{
|
||||
GameMain.Server.KarmaManager.OnWireDisconnected(user, this);
|
||||
}
|
||||
|
||||
if (connections[0] != null && connections[1] != null)
|
||||
{
|
||||
GameServer.Log(user.LogName + " disconnected a wire from " +
|
||||
connections[0].Item.Name + " (" + connections[0].Name + ") to "+
|
||||
connections[1].Item.Name + " (" + connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
else if (connections[0] != null)
|
||||
{
|
||||
GameServer.Log(user.LogName + " disconnected a wire from " +
|
||||
connections[0].Item.Name + " (" + connections[0].Name + ")", ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
else if (connections[1] != null)
|
||||
{
|
||||
GameServer.Log(user.LogName + " disconnected a wire from " +
|
||||
connections[1].Item.Name + " (" + connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SetConnectedDirty();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i] == null) { continue; }
|
||||
int wireIndex = connections[i].FindWireIndex(item);
|
||||
if (wireIndex == -1) { continue; }
|
||||
#if SERVER
|
||||
if (!connections[i].Item.Removed)
|
||||
{
|
||||
connections[i].Item.CreateServerEvent(connections[i].Item.GetComponent<ConnectionPanel>());
|
||||
}
|
||||
#endif
|
||||
connections[i].SetWire(wireIndex, null);
|
||||
connections[i] = null;
|
||||
}
|
||||
|
||||
Drawable = sections.Count > 0;
|
||||
}
|
||||
|
||||
private Vector2 RoundNode(Vector2 position)
|
||||
{
|
||||
position.X = MathUtils.Round(position.X, Submarine.GridSize.X / 2.0f);
|
||||
position.Y = MathUtils.Round(position.Y, Submarine.GridSize.Y / 2.0f);
|
||||
return position;
|
||||
}
|
||||
|
||||
public void SetConnectedDirty()
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i]?.Item != null)
|
||||
{
|
||||
var pt = connections[i].Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null) pt.SetConnectionDirty(connections[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanNodes()
|
||||
{
|
||||
bool removed;
|
||||
do
|
||||
{
|
||||
removed = false;
|
||||
for (int i = nodes.Count - 2; i > 0; i--)
|
||||
{
|
||||
if (Math.Abs(nodes[i - 1].X - nodes[i].X) < 1.0f && Math.Abs(nodes[i + 1].X - nodes[i].X) < 1.0f &&
|
||||
Math.Sign(nodes[i - 1].Y - nodes[i].Y) != Math.Sign(nodes[i + 1].Y - nodes[i].Y))
|
||||
{
|
||||
nodes.RemoveAt(i);
|
||||
removed = true;
|
||||
}
|
||||
else if (Math.Abs(nodes[i - 1].Y - nodes[i].Y) < 1.0f && Math.Abs(nodes[i + 1].Y - nodes[i].Y) < 1.0f &&
|
||||
Math.Sign(nodes[i - 1].X - nodes[i].X) != Math.Sign(nodes[i + 1].X - nodes[i].X))
|
||||
{
|
||||
nodes.RemoveAt(i);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
|
||||
} while (removed);
|
||||
}
|
||||
|
||||
private void FixNodeEnds()
|
||||
{
|
||||
if (connections[0] == null || connections[1] == null || nodes.Count == 0) { return; }
|
||||
|
||||
Vector2 nodePos = nodes[0];
|
||||
|
||||
Submarine refSub = connections[0].Item.Submarine ?? connections[1].Item.Submarine;
|
||||
if (refSub != null) { nodePos += refSub.HiddenSubPosition; }
|
||||
|
||||
float dist1 = Vector2.DistanceSquared(connections[0].Item.Position, nodePos);
|
||||
float dist2 = Vector2.DistanceSquared(connections[1].Item.Position, nodePos);
|
||||
|
||||
//first node is closer to the second item
|
||||
//= the nodes are "backwards", need to reverse them
|
||||
if (dist1 > dist2)
|
||||
{
|
||||
nodes.Reverse();
|
||||
UpdateSections();
|
||||
}
|
||||
}
|
||||
|
||||
private int GetClosestNodeIndex(Vector2 pos, float maxDist, out float closestDist)
|
||||
{
|
||||
closestDist = 0.0f;
|
||||
int closestIndex = -1;
|
||||
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
float dist = Vector2.Distance(nodes[i], pos);
|
||||
if (dist > maxDist) continue;
|
||||
|
||||
if (closestIndex == -1 || dist < closestDist)
|
||||
{
|
||||
closestIndex = i;
|
||||
closestDist = dist;
|
||||
}
|
||||
}
|
||||
|
||||
return closestIndex;
|
||||
}
|
||||
|
||||
private int GetClosestSectionIndex(Vector2 mousePos, float maxDist, out float closestDist)
|
||||
{
|
||||
closestDist = 0.0f;
|
||||
int closestIndex = -1;
|
||||
|
||||
for (int i = 0; i < nodes.Count-1; i++)
|
||||
{
|
||||
if ((Math.Abs(nodes[i].X - nodes[i + 1].X)<5 || Math.Sign(mousePos.X - nodes[i].X) != Math.Sign(mousePos.X - nodes[i + 1].X)) &&
|
||||
(Math.Abs(nodes[i].Y - nodes[i + 1].Y)<5 || Math.Sign(mousePos.Y - nodes[i].Y) != Math.Sign(mousePos.Y - nodes[i + 1].Y)))
|
||||
{
|
||||
float dist = MathUtils.LineToPointDistance(nodes[i], nodes[i + 1], mousePos);
|
||||
if (dist > maxDist) continue;
|
||||
|
||||
if (closestIndex == -1 || dist < closestDist)
|
||||
{
|
||||
closestIndex = i;
|
||||
closestDist = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestIndex;
|
||||
}
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
nodes[i] = new Vector2(-nodes[i].X, nodes[i].Y);
|
||||
}
|
||||
UpdateSections();
|
||||
}
|
||||
|
||||
public override void FlipY(bool relativeToSub)
|
||||
{
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
nodes[i] = new Vector2(nodes[i].X, -nodes[i].Y);
|
||||
}
|
||||
UpdateSections();
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues);
|
||||
|
||||
string nodeString = componentElement.GetAttributeString("nodes", "");
|
||||
if (nodeString == "") return;
|
||||
|
||||
string[] nodeCoords = nodeString.Split(';');
|
||||
for (int i = 0; i < nodeCoords.Length / 2; i++)
|
||||
{
|
||||
float.TryParse(nodeCoords[i * 2], NumberStyles.Float, CultureInfo.InvariantCulture, out float x);
|
||||
float.TryParse(nodeCoords[i * 2 + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out float y);
|
||||
nodes.Add(new Vector2(x, y));
|
||||
}
|
||||
|
||||
Drawable = nodes.Any();
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
XElement componentElement = base.Save(parentElement);
|
||||
|
||||
if (nodes == null || nodes.Count == 0) return componentElement;
|
||||
|
||||
string[] nodeCoords = new string[nodes.Count * 2];
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
nodeCoords[i * 2] = nodes[i].X.ToString(CultureInfo.InvariantCulture);
|
||||
nodeCoords[i * 2 + 1] = nodes[i].Y.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
componentElement.Add(new XAttribute("nodes", string.Join(";", nodeCoords)));
|
||||
|
||||
return componentElement;
|
||||
}
|
||||
|
||||
protected override void ShallowRemoveComponentSpecific()
|
||||
{
|
||||
/*for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i] == null) continue;
|
||||
int wireIndex = connections[i].FindWireIndex(item);
|
||||
|
||||
if (wireIndex > -1)
|
||||
{
|
||||
connections[i].AddLink(wireIndex, null);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
ClearConnections();
|
||||
base.RemoveComponentSpecific();
|
||||
#if CLIENT
|
||||
overrideSprite?.Remove();
|
||||
overrideSprite = null;
|
||||
wireSprite = null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class XorComponent : AndComponent
|
||||
{
|
||||
public XorComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
int sendOutput = 0;
|
||||
for (int i = 0; i < timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] <= timeFrame) sendOutput += 1;
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
string signalOut = sendOutput == 1 ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut)) return;
|
||||
|
||||
item.SendSignal(0, signalOut, "signal_out", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user