(61d00a474) v0.9.7.1
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class PowerContainer : Powered, IDrawableComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
//[power/min]
|
||||
private float capacity;
|
||||
|
||||
private float charge;
|
||||
|
||||
//private float rechargeVoltage;
|
||||
|
||||
//how fast the battery can be recharged
|
||||
private float maxRechargeSpeed;
|
||||
|
||||
//how fast it's currently being recharged (can be changed, so that
|
||||
//charging can be slowed down or disabled if there's a shortage of power)
|
||||
private float rechargeSpeed;
|
||||
private float lastSentCharge;
|
||||
|
||||
//charge indicator description
|
||||
protected Vector2 indicatorPosition, indicatorSize;
|
||||
|
||||
protected bool isHorizontal;
|
||||
|
||||
public float CurrPowerOutput
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize("0,0", true, description: "The position of the progress bar indicating the charge of the item. In pixels as an offset from the upper left corner of the sprite.")]
|
||||
public Vector2 IndicatorPosition
|
||||
{
|
||||
get { return indicatorPosition; }
|
||||
set { indicatorPosition = value; }
|
||||
}
|
||||
|
||||
[Serialize("0,0", true, description: "The size of the progress bar indicating the charge of the item (in pixels).")]
|
||||
public Vector2 IndicatorSize
|
||||
{
|
||||
get { return indicatorSize; }
|
||||
set { indicatorSize = value; }
|
||||
}
|
||||
|
||||
[Serialize(false, true, description: "Should the progress bar indicating the charge of the item fill up horizontally or vertically.")]
|
||||
public bool IsHorizontal
|
||||
{
|
||||
get { return isHorizontal; }
|
||||
set { isHorizontal = value; }
|
||||
}
|
||||
|
||||
[Editable, Serialize(10.0f, true, description: "Maximum output of the device when fully charged (kW).")]
|
||||
public float MaxOutPut { set; get; }
|
||||
|
||||
[Editable, Serialize(10.0f, true, description: "The maximum capacity of the device (kW * min). For example, a value of 1000 means the device can output 100 kilowatts of power for 10 minutes, or 1000 kilowatts for 1 minute.")]
|
||||
public float Capacity
|
||||
{
|
||||
get { return capacity; }
|
||||
set { capacity = Math.Max(value, 1.0f); }
|
||||
}
|
||||
|
||||
[Editable, Serialize(0.0f, true, description: "The current charge of the device.")]
|
||||
public float Charge
|
||||
{
|
||||
get { return charge; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
charge = MathHelper.Clamp(value, 0.0f, capacity);
|
||||
|
||||
//send a network event if the charge has changed by more than 5%
|
||||
if (Math.Abs(charge - lastSentCharge) / capacity > 0.05f)
|
||||
{
|
||||
#if SERVER
|
||||
if (GameMain.Server != null) item.CreateServerEvent(this);
|
||||
#endif
|
||||
lastSentCharge = charge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float ChargePercentage => MathUtils.Percentage(Charge, Capacity);
|
||||
|
||||
[Editable, Serialize(10.0f, true, description: "How fast the device can be recharged. For example, a recharge speed of 100 kW and a capacity of 1000 kW*min would mean it takes 10 minutes to fully charge the device.")]
|
||||
public float MaxRechargeSpeed
|
||||
{
|
||||
get { return maxRechargeSpeed; }
|
||||
set { maxRechargeSpeed = Math.Max(value, 1.0f); }
|
||||
}
|
||||
|
||||
[Editable, Serialize(10.0f, true, description: "The current recharge speed of the device.")]
|
||||
public float RechargeSpeed
|
||||
{
|
||||
get { return rechargeSpeed; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
rechargeSpeed = MathHelper.Clamp(value, 0.0f, maxRechargeSpeed);
|
||||
rechargeSpeed = MathUtils.RoundTowardsClosest(rechargeSpeed, Math.Max(maxRechargeSpeed * 0.1f, 1.0f));
|
||||
if (isRunning)
|
||||
{
|
||||
HasBeenTuned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float RechargeRatio => RechargeSpeed / MaxRechargeSpeed;
|
||||
|
||||
public const float aiRechargeTargetRatio = 0.5f;
|
||||
private bool isRunning;
|
||||
public bool HasBeenTuned { get; private set; }
|
||||
|
||||
public PowerContainer(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
InitProjSpecific();
|
||||
}
|
||||
|
||||
partial void InitProjSpecific();
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return picker != null;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
isRunning = true;
|
||||
float chargeRatio = charge / capacity;
|
||||
float gridPower = 0.0f;
|
||||
float gridLoad = 0.0f;
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
if (!c.IsPower || !c.IsOutput) { continue; }
|
||||
foreach (Connection c2 in c.Recipients)
|
||||
{
|
||||
if (c2.Item.Condition <= 0.0f) { continue; }
|
||||
|
||||
PowerTransfer pt = c2.Item.GetComponent<PowerTransfer>();
|
||||
if (pt == null)
|
||||
{
|
||||
foreach (Powered powered in c2.Item.GetComponents<Powered>())
|
||||
{
|
||||
if (!powered.IsActive) continue;
|
||||
gridLoad += powered.CurrPowerConsumption;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!pt.IsActive || !pt.CanTransfer) { continue; }
|
||||
gridPower -= pt.CurrPowerConsumption;
|
||||
gridLoad += pt.PowerLoad;
|
||||
}
|
||||
}
|
||||
|
||||
if (chargeRatio > 0.0f)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
|
||||
if (charge >= capacity)
|
||||
{
|
||||
//rechargeVoltage = 0.0f;
|
||||
charge = capacity;
|
||||
CurrPowerConsumption = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
currPowerConsumption = MathHelper.Lerp(currPowerConsumption, rechargeSpeed, 0.05f);
|
||||
Charge += currPowerConsumption * Math.Min(Voltage, 1.0f) / 3600.0f;
|
||||
}
|
||||
|
||||
if (charge <= 0.0f)
|
||||
{
|
||||
CurrPowerOutput = 0.0f;
|
||||
charge = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
//output starts dropping when the charge is less than 10%
|
||||
float maxOutputRatio = 1.0f;
|
||||
if (chargeRatio < 0.1f)
|
||||
{
|
||||
maxOutputRatio = Math.Max(chargeRatio * 10.0f, 0.0f);
|
||||
}
|
||||
|
||||
CurrPowerOutput += (gridLoad - gridPower) * deltaTime;
|
||||
|
||||
float maxOutput = Math.Min(MaxOutPut * maxOutputRatio, gridLoad);
|
||||
CurrPowerOutput = MathHelper.Clamp(CurrPowerOutput, 0.0f, maxOutput);
|
||||
Charge -= CurrPowerOutput / 3600.0f;
|
||||
|
||||
item.SendSignal(0, ((int)Math.Round(Charge)).ToString(), "charge", null);
|
||||
item.SendSignal(0, ((int)Math.Round((Charge / capacity) * 100)).ToString(), "charge_%", null);
|
||||
item.SendSignal(0, ((int)Math.Round((RechargeSpeed / maxRechargeSpeed) * 100)).ToString(), "charge_rate", null);
|
||||
}
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
|
||||
|
||||
if (objective.Override)
|
||||
{
|
||||
HasBeenTuned = false;
|
||||
}
|
||||
if (HasBeenTuned) { return true; }
|
||||
|
||||
if (string.IsNullOrEmpty(objective.Option) || objective.Option.Equals("charge", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Math.Abs(rechargeSpeed - maxRechargeSpeed * aiRechargeTargetRatio) > 0.05f)
|
||||
{
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
RechargeSpeed = maxRechargeSpeed * aiRechargeTargetRatio;
|
||||
#if CLIENT
|
||||
if (rechargeSpeedSlider != null)
|
||||
{
|
||||
rechargeSpeedSlider.BarScroll = RechargeSpeed / Math.Max(maxRechargeSpeed, 1.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
character.Speak(TextManager.GetWithVariables("DialogChargeBatteries", new string[2] { "[itemname]", "[rate]" },
|
||||
new string[2] { item.Name, ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString() },
|
||||
new bool[2] { true, false }), null, 1.0f, "chargebattery", 10.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rechargeSpeed > 0.0f)
|
||||
{
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
RechargeSpeed = 0.0f;
|
||||
#if CLIENT
|
||||
if (rechargeSpeedSlider != null)
|
||||
{
|
||||
rechargeSpeedSlider.BarScroll = RechargeSpeed / Math.Max(maxRechargeSpeed, 1.0f);
|
||||
}
|
||||
#endif
|
||||
character.Speak(TextManager.GetWithVariables("DialogStopChargingBatteries", new string[2] { "[itemname]", "[rate]" },
|
||||
new string[2] { item.Name, ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString() },
|
||||
new bool[2] { true, false }), null, 1.0f, "chargebattery", 10.0f);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power, float signalStrength = 1.0f)
|
||||
{
|
||||
if (connection.IsPower) { return; }
|
||||
|
||||
if (connection.Name == "set_rate")
|
||||
{
|
||||
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempSpeed))
|
||||
{
|
||||
if (!MathUtils.IsValid(tempSpeed)) { return; }
|
||||
|
||||
float rechargeRate = MathHelper.Clamp(tempSpeed / 100.0f, 0.0f, 1.0f);
|
||||
RechargeSpeed = rechargeRate * MaxRechargeSpeed;
|
||||
#if CLIENT
|
||||
if (rechargeSpeedSlider != null)
|
||||
{
|
||||
rechargeSpeedSlider.BarScroll = rechargeRate;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class PowerTransfer : Powered
|
||||
{
|
||||
public List<Connection> PowerConnections { get; private set; }
|
||||
|
||||
private readonly Dictionary<Connection, bool> connectionDirty = new Dictionary<Connection, bool>();
|
||||
|
||||
//a list of connections a given connection is connected to, either directly or via other power transfer components
|
||||
private readonly Dictionary<Connection, HashSet<Connection>> connectedRecipients = new Dictionary<Connection, HashSet<Connection>>();
|
||||
|
||||
protected float powerLoad;
|
||||
|
||||
protected bool isBroken;
|
||||
|
||||
public float PowerLoad
|
||||
{
|
||||
get { return powerLoad; }
|
||||
set { powerLoad = value; }
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the item be damaged if too much power is supplied to the power grid.")]
|
||||
public bool CanBeOverloaded
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable(MinValueFloat = 1.0f), Serialize(2.0f, true, description:
|
||||
"How much power has to be supplied to the grid relative to the load before item starts taking damage. "
|
||||
+ "E.g. a value of 2 means that the grid has to be receiving twice as much power as the devices in the grid are consuming.")]
|
||||
public float OverloadVoltage
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.15f, true, description: "The probability for a fire to start when the item breaks."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
|
||||
public float FireProbability
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Is the item currently overloaded. Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
public bool Overload
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
//can the component transfer power
|
||||
private bool canTransfer;
|
||||
public bool CanTransfer
|
||||
{
|
||||
get { return canTransfer; }
|
||||
set
|
||||
{
|
||||
if (canTransfer == value) return;
|
||||
canTransfer = value;
|
||||
SetAllConnectionsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.IsActive;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (base.IsActive == value) return;
|
||||
base.IsActive = value;
|
||||
powerLoad = 0.0f;
|
||||
currPowerConsumption = 0.0f;
|
||||
|
||||
SetAllConnectionsDirty();
|
||||
if (!base.IsActive)
|
||||
{
|
||||
//we need to refresh the connections here because Update won't be called on inactive components
|
||||
RefreshConnections();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PowerTransfer(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
canTransfer = true;
|
||||
|
||||
InitProjectSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjectSpecific(XElement element);
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
base.UpdateBroken(deltaTime, cam);
|
||||
|
||||
Overload = false;
|
||||
|
||||
if (!isBroken)
|
||||
{
|
||||
powerLoad = 0.0f;
|
||||
currPowerConsumption = 0.0f;
|
||||
SetAllConnectionsDirty();
|
||||
RefreshConnections();
|
||||
isBroken = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
RefreshConnections();
|
||||
|
||||
if (!CanTransfer) { return; }
|
||||
|
||||
if (isBroken)
|
||||
{
|
||||
SetAllConnectionsDirty();
|
||||
isBroken = false;
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
//if the item can't be fixed, don't allow it to break
|
||||
if (!item.Repairables.Any() || !CanBeOverloaded) { return; }
|
||||
|
||||
float maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
|
||||
Overload = -currPowerConsumption > Math.Max(powerLoad, 200.0f) * maxOverVoltage;
|
||||
if (Overload && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
|
||||
{
|
||||
//damage the item if voltage is too high (except if running as a client)
|
||||
float prevCondition = item.Condition;
|
||||
item.Condition -= deltaTime * 10.0f;
|
||||
|
||||
if (item.Condition <= 0.0f && prevCondition > 0.0f)
|
||||
{
|
||||
#if CLIENT
|
||||
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
Vector2 baseVel = Rand.Vector(300.0f);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var particle = GameMain.ParticleManager.CreateParticle("spark", item.WorldPosition,
|
||||
baseVel + Rand.Vector(100.0f), 0.0f, item.CurrentHull);
|
||||
if (particle != null) particle.Size *= Rand.Range(0.5f, 1.0f);
|
||||
}
|
||||
#endif
|
||||
float currentIntensity = GameMain.GameSession?.EventManager != null ?
|
||||
GameMain.GameSession.EventManager.CurrentIntensity : 0.5f;
|
||||
|
||||
//higher probability for fires if the current intensity is low
|
||||
if (FireProbability > 0.0f &&
|
||||
Rand.Range(0.0f, 1.0f) < MathHelper.Lerp(FireProbability, FireProbability * 0.1f, currentIntensity))
|
||||
{
|
||||
new FireSource(item.WorldPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return picker != null;
|
||||
}
|
||||
|
||||
protected void RefreshConnections()
|
||||
{
|
||||
var connections = item.Connections;
|
||||
foreach (Connection c in connections)
|
||||
{
|
||||
if (!connectionDirty.ContainsKey(c))
|
||||
{
|
||||
connectionDirty[c] = true;
|
||||
}
|
||||
else if (!connectionDirty[c])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
HashSet<Connection> connected = new HashSet<Connection>();
|
||||
if (!connectedRecipients.ContainsKey(c))
|
||||
{
|
||||
connectedRecipients.Add(c, connected);
|
||||
}
|
||||
else
|
||||
{
|
||||
//mark all previous recipients as dirty
|
||||
foreach (Connection recipient in connectedRecipients[c])
|
||||
{
|
||||
var pt = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null) pt.connectionDirty[recipient] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//find all connections that are connected to this one (directly or via another PowerTransfer)
|
||||
connected.Add(c);
|
||||
GetConnected(c, connected);
|
||||
connectedRecipients[c] = connected;
|
||||
|
||||
//go through all the PowerTransfers and we're connected to and set their connections to match the ones we just calculated
|
||||
//(no need to go through the recursive GetConnected method again)
|
||||
foreach (Connection recipient in connected)
|
||||
{
|
||||
var recipientPowerTransfer = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (recipientPowerTransfer == null) continue;
|
||||
|
||||
if (!connectedRecipients.ContainsKey(recipient))
|
||||
{
|
||||
connectedRecipients.Add(recipient, connected);
|
||||
}
|
||||
|
||||
recipientPowerTransfer.connectedRecipients[recipient] = connected;
|
||||
recipientPowerTransfer.connectionDirty[recipient] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Finds all the connections that can receive a signal sent into the given connection and stores them in the hashset.
|
||||
private void GetConnected(Connection c, HashSet<Connection> connected)
|
||||
{
|
||||
var recipients = c.Recipients;
|
||||
|
||||
foreach (Connection recipient in recipients)
|
||||
{
|
||||
if (recipient == null || connected.Contains(recipient)) continue;
|
||||
|
||||
Item it = recipient.Item;
|
||||
if (it == null || it.Condition <= 0.0f) continue;
|
||||
|
||||
connected.Add(recipient);
|
||||
|
||||
var powerTransfer = it.GetComponent<PowerTransfer>();
|
||||
if (powerTransfer != null && powerTransfer.CanTransfer && powerTransfer.IsActive)
|
||||
{
|
||||
GetConnected(recipient, connected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAllConnectionsDirty()
|
||||
{
|
||||
if (item.Connections == null) return;
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
connectionDirty[c] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetConnectionDirty(Connection connection)
|
||||
{
|
||||
var connections = item.Connections;
|
||||
if (connections == null || !connections.Contains(connection)) return;
|
||||
connectionDirty[connection] = true;
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
var connections = Item.Connections;
|
||||
PowerConnections = connections == null ? new List<Connection>() : connections.FindAll(c => c.IsPower);
|
||||
if (connections == null)
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(this is RelayComponent))
|
||||
{
|
||||
if (PowerConnections.Any(p => !p.IsOutput) && PowerConnections.Any(p => p.IsOutput))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item \"" + Name + "\" - PowerTransfer components should not have separate power inputs and outputs, but transfer power between wires connected to the same power connection. " +
|
||||
"If you want power to pass from input to output, change the component to a RelayComponent.");
|
||||
}
|
||||
}
|
||||
|
||||
SetAllConnectionsDirty();
|
||||
}
|
||||
|
||||
public override void ReceivePowerProbeSignal(Connection connection, Item source, float power)
|
||||
{
|
||||
//we've already received this signal
|
||||
if (lastPowerProbeRecipients.Contains(this)) { return; }
|
||||
if (item.Condition <= 0.0f) { return; }
|
||||
|
||||
lastPowerProbeRecipients.Add(this);
|
||||
|
||||
if (power < 0.0f)
|
||||
{
|
||||
powerLoad -= power;
|
||||
}
|
||||
else
|
||||
{
|
||||
currPowerConsumption -= power;
|
||||
}
|
||||
powerOut?.SendPowerProbeSignal(source, power);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power, float signalStrength = 1.0f)
|
||||
{
|
||||
if (item.Condition <= 0.0f || connection.IsPower) { return; }
|
||||
if (!connectedRecipients.ContainsKey(connection)) { return; }
|
||||
|
||||
if (connection.Name.Length > 5 && connection.Name.Substring(0, 6) == "signal")
|
||||
{
|
||||
foreach (Connection recipient in connectedRecipients[connection])
|
||||
{
|
||||
if (recipient.Item == item || recipient.Item == source) { continue; }
|
||||
|
||||
foreach (ItemComponent ic in recipient.Item.Components)
|
||||
{
|
||||
//other junction boxes don't need to receive the signal in the pass-through signal connections
|
||||
//because we relay it straight to the connected items without going through the whole chain of junction boxes
|
||||
if (ic is PowerTransfer && !(ic is RelayComponent) && connection.Name.Contains("signal")) { continue; }
|
||||
ic.ReceiveSignal(stepsTaken, signal, recipient, source, sender, 0.0f, signalStrength);
|
||||
}
|
||||
|
||||
foreach (StatusEffect effect in recipient.Effects)
|
||||
{
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
#if CLIENT
|
||||
using Barotrauma.Sounds;
|
||||
#endif
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Powered : ItemComponent
|
||||
{
|
||||
private static float updateTimer;
|
||||
protected static float UpdateInterval = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// List of all powered ItemComponents
|
||||
/// </summary>
|
||||
private static readonly List<Powered> poweredList = new List<Powered>();
|
||||
|
||||
/// <summary>
|
||||
/// Items that have already received the "probe signal" that's used to distribute power and load across the grid
|
||||
/// </summary>
|
||||
protected static HashSet<PowerTransfer> lastPowerProbeRecipients = new HashSet<PowerTransfer>();
|
||||
|
||||
/// <summary>
|
||||
/// The amount of power currently consumed by the item. Negative values mean that the item is providing power to connected items
|
||||
/// </summary>
|
||||
protected float currPowerConsumption;
|
||||
|
||||
/// <summary>
|
||||
/// Current voltage of the item (load / power)
|
||||
/// </summary>
|
||||
private float voltage;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum voltage required for the item to work
|
||||
/// </summary>
|
||||
private float minVoltage;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of power the item can draw from connected items
|
||||
/// </summary>
|
||||
protected float powerConsumption;
|
||||
|
||||
protected Connection powerIn, powerOut;
|
||||
|
||||
[Editable, Serialize(0.5f, true, description: "The minimum voltage required for the device to function. " +
|
||||
"The voltage is calculated as power / powerconsumption, meaning that a device " +
|
||||
"with a power consumption of 1000 kW would need at least 500 kW of power to work if the minimum voltage is set to 0.5.")]
|
||||
public float MinVoltage
|
||||
{
|
||||
get { return powerConsumption <= 0.0f ? 0.0f : minVoltage; }
|
||||
set { minVoltage = value; }
|
||||
}
|
||||
|
||||
[Editable, Serialize(0.0f, true, description: "How much power the device draws (or attempts to draw) from the electrical grid when active.")]
|
||||
public float PowerConsumption
|
||||
{
|
||||
get { return powerConsumption; }
|
||||
set { powerConsumption = value; }
|
||||
}
|
||||
|
||||
[Serialize(false, true, description: "Is the device currently active. Inactive devices don't consume power.")]
|
||||
public override bool IsActive
|
||||
{
|
||||
get { return base.IsActive; }
|
||||
set
|
||||
{
|
||||
base.IsActive = value;
|
||||
if (!value)
|
||||
{
|
||||
currPowerConsumption = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true, description: "The current power consumption of the device. Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
public float CurrPowerConsumption
|
||||
{
|
||||
get {return currPowerConsumption; }
|
||||
set { currPowerConsumption = value; }
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true, description: "The current voltage of the item (calculated as power consumption / available power). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
public float Voltage
|
||||
{
|
||||
get { return voltage; }
|
||||
set { voltage = Math.Max(0.0f, value); }
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the item be damaged by electomagnetic pulses.")]
|
||||
public bool VulnerableToEMP
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Powered(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
poweredList.Add(this);
|
||||
InitProjectSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjectSpecific(XElement element);
|
||||
|
||||
protected void UpdateOnActiveEffects(float deltaTime)
|
||||
{
|
||||
if (currPowerConsumption <= 0.0f)
|
||||
{
|
||||
//if the item consumes no power, ignore the voltage requirement and
|
||||
//apply OnActive statuseffects as long as this component is active
|
||||
if (powerConsumption <= 0.0f)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (voltage > minVoltage)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
#if CLIENT
|
||||
if (voltage > minVoltage)
|
||||
{
|
||||
if (!powerOnSoundPlayed && powerOnSound != null)
|
||||
{
|
||||
SoundPlayer.PlaySound(powerOnSound.Sound, item.WorldPosition, powerOnSound.Volume, powerOnSound.Range, item.CurrentHull);
|
||||
powerOnSoundPlayed = true;
|
||||
}
|
||||
}
|
||||
else if (voltage < 0.1f)
|
||||
{
|
||||
powerOnSoundPlayed = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
if (item.Connections == null) { return; }
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
if (!c.IsPower) { continue; }
|
||||
if (this is PowerTransfer pt)
|
||||
{
|
||||
if (c.Name == "power_in")
|
||||
{
|
||||
powerIn = c;
|
||||
}
|
||||
else if (c.Name == "power_out")
|
||||
{
|
||||
powerOut = c;
|
||||
}
|
||||
else if (c.Name == "power")
|
||||
{
|
||||
powerIn = powerOut = c;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c.IsOutput)
|
||||
{
|
||||
if (c.Name == "power_in")
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError($"Item \"{item.Name}\" has a power output connection called power_in. If the item is supposed to receive power through the connection, change it to an input connection.");
|
||||
#else
|
||||
DebugConsole.NewMessage($"Item \"{item.Name}\" has a power output connection called power_in. If the item is supposed to receive power through the connection, change it to an input connection.", Color.Orange);
|
||||
#endif
|
||||
}
|
||||
powerOut = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c.Name == "power_out")
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError($"Item \"{item.Name}\" has a power input connection called power_out. If the item is supposed to output power through the connection, change it to an output connection.");
|
||||
#else
|
||||
DebugConsole.NewMessage($"Item \"{item.Name}\" has a power input connection called power_out. If the item is supposed to output power through the connection, change it to an output connection.", Color.Orange);
|
||||
#endif
|
||||
}
|
||||
powerIn = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ReceivePowerProbeSignal(Connection connection, Item source, float power) { }
|
||||
|
||||
public static void UpdatePower(float deltaTime)
|
||||
{
|
||||
if (updateTimer > 0.0f)
|
||||
{
|
||||
updateTimer -= deltaTime;
|
||||
return;
|
||||
}
|
||||
updateTimer = UpdateInterval;
|
||||
|
||||
//reset power first
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer pt)
|
||||
{
|
||||
powered.CurrPowerConsumption = 0.0f;
|
||||
pt.PowerLoad = 0.0f;
|
||||
if (pt is RelayComponent relay)
|
||||
{
|
||||
relay.DisplayLoad = 0.0f;
|
||||
}
|
||||
}
|
||||
//only reset voltage if the item has a power connector
|
||||
//(other items, such as handheld devices, get power through other means and shouldn't be updated here)
|
||||
if (powered.powerIn != null || powered.powerOut != null) { powered.voltage = 0.0f; }
|
||||
}
|
||||
|
||||
//go through all the devices that are consuming/providing power
|
||||
//and send out a "probe signal" which the PowerTransfer components use to add up the grid power/load
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer) { continue; }
|
||||
if (powered.currPowerConsumption > 0.0f)
|
||||
{
|
||||
//consuming power
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerIn?.SendPowerProbeSignal(powered.item, -powered.currPowerConsumption);
|
||||
}
|
||||
}
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer) { continue; }
|
||||
else if (powered.currPowerConsumption < 0.0f)
|
||||
{
|
||||
//providing power
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerOut?.SendPowerProbeSignal(powered.item, -powered.currPowerConsumption);
|
||||
}
|
||||
if (powered is PowerContainer pc)
|
||||
{
|
||||
if (pc.CurrPowerOutput <= 0.0f) { continue; }
|
||||
//providing power
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerOut?.SendPowerProbeSignal(powered.item, pc.CurrPowerOutput);
|
||||
}
|
||||
}
|
||||
//go through powered items and calculate their current voltage
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer pt1 || (pt1 = powered.Item.GetComponent<PowerTransfer>()) != null)
|
||||
{
|
||||
powered.voltage = -pt1.CurrPowerConsumption / Math.Max(pt1.PowerLoad, 1.0f);
|
||||
continue;
|
||||
}
|
||||
if (powered.powerConsumption <= 0.0f && !(powered is PowerContainer))
|
||||
{
|
||||
powered.voltage = 1.0f;
|
||||
continue;
|
||||
}
|
||||
if (powered.powerIn == null) { continue; }
|
||||
|
||||
foreach (Connection powerSource in powered.powerIn.Recipients)
|
||||
{
|
||||
if (!powerSource.IsPower || !powerSource.IsOutput) { continue; }
|
||||
var pt = powerSource.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null)
|
||||
{
|
||||
float voltage = -pt.CurrPowerConsumption / Math.Max(pt.PowerLoad, 1.0f);
|
||||
powered.voltage = Math.Max(powered.voltage, voltage);
|
||||
continue;
|
||||
}
|
||||
var pc = powerSource.Item.GetComponent<PowerContainer>();
|
||||
if (pc != null)
|
||||
{
|
||||
float voltage = pc.CurrPowerOutput / Math.Max(powered.CurrPowerConsumption, 1.0f);
|
||||
powered.voltage += voltage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the amount of power that can be supplied by batteries directly connected to the item
|
||||
/// </summary>
|
||||
protected float GetAvailableBatteryPower()
|
||||
{
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
|
||||
float availablePower = 0.0f;
|
||||
foreach (PowerContainer battery in batteries)
|
||||
{
|
||||
float batteryPower = Math.Min(battery.Charge * 3600.0f, battery.MaxOutPut);
|
||||
availablePower += batteryPower;
|
||||
}
|
||||
|
||||
return availablePower;
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
poweredList.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user