Unstable 0.17.0.0
This commit is contained in:
+154
-97
@@ -13,8 +13,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float charge;
|
||||
|
||||
//private float rechargeVoltage;
|
||||
|
||||
//how fast the battery can be recharged
|
||||
private float maxRechargeSpeed;
|
||||
|
||||
@@ -27,45 +25,52 @@ namespace Barotrauma.Items.Components
|
||||
protected Vector2 indicatorPosition, indicatorSize;
|
||||
|
||||
protected bool isHorizontal;
|
||||
|
||||
|
||||
protected override PowerPriority Priority { get { return PowerPriority.Battery; } }
|
||||
|
||||
private float currPowerOutput;
|
||||
public float CurrPowerOutput
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
get { return currPowerOutput; }
|
||||
private set
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(value >= 0.0f);
|
||||
currPowerOutput = Math.Max(0, value);
|
||||
}
|
||||
}
|
||||
|
||||
[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.")]
|
||||
[Serialize("0,0", IsPropertySaveable.Yes, 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).")]
|
||||
[Serialize("0,0", IsPropertySaveable.Yes, 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.")]
|
||||
[Serialize(false, IsPropertySaveable.Yes, 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).")]
|
||||
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, 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.")]
|
||||
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, 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.")]
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "The current charge of the device.")]
|
||||
public float Charge
|
||||
{
|
||||
get { return charge; }
|
||||
@@ -87,14 +92,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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.")]
|
||||
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, 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.")]
|
||||
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, description: "The current recharge speed of the device.")]
|
||||
public float RechargeSpeed
|
||||
{
|
||||
get { return rechargeSpeed; }
|
||||
@@ -110,14 +115,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, true, description: "If true, the recharge speed (and power consumption) of the device goes up exponentially as the recharge rate is increased.")]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "If true, the recharge speed (and power consumption) of the device goes up exponentially as the recharge rate is increased.")]
|
||||
public bool ExponentialRechargeSpeed { get; set; }
|
||||
|
||||
[Editable(minValue: 0.0f, maxValue: 10.0f, decimals: 2), Serialize(0.5f, true)]
|
||||
[Editable(minValue: 0.0f, maxValue: 10.0f, decimals: 2), Serialize(0.5f, IsPropertySaveable.Yes)]
|
||||
public float RechargeAdjustSpeed { get; set; }
|
||||
|
||||
private float efficiency;
|
||||
[Editable(minValue: 0.0f, maxValue: 1.0f, decimals: 2), Serialize(0.95f, true, description: "The amount of power you can get out of a item relative to the amount of power that's put into it.")]
|
||||
[Editable(minValue: 0.0f, maxValue: 1.0f, decimals: 2), Serialize(0.95f, IsPropertySaveable.Yes, description: "The amount of power you can get out of a item relative to the amount of power that's put into it.")]
|
||||
public float Efficiency
|
||||
{
|
||||
get { return efficiency; }
|
||||
@@ -130,7 +135,7 @@ namespace Barotrauma.Items.Components
|
||||
private bool isRunning;
|
||||
public bool HasBeenTuned { get; private set; }
|
||||
|
||||
public PowerContainer(Item item, XElement element)
|
||||
public PowerContainer(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
@@ -154,92 +159,142 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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)
|
||||
float loadReading = 0;
|
||||
if (powerOut != null && powerOut.Grid != null)
|
||||
{
|
||||
//rechargeVoltage = 0.0f;
|
||||
charge = capacity;
|
||||
CurrPowerConsumption = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
float missingCharge = capacity - charge;
|
||||
float targetRechargeSpeed = rechargeSpeed;
|
||||
if (ExponentialRechargeSpeed)
|
||||
{
|
||||
targetRechargeSpeed = MathF.Pow(rechargeSpeed / maxRechargeSpeed, 2) * maxRechargeSpeed;
|
||||
}
|
||||
if (missingCharge < 1.0f)
|
||||
{
|
||||
targetRechargeSpeed *= missingCharge;
|
||||
}
|
||||
if (currPowerConsumption < targetRechargeSpeed)
|
||||
{
|
||||
currPowerConsumption = Math.Min(currPowerConsumption + deltaTime * maxRechargeSpeed * RechargeAdjustSpeed, targetRechargeSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
currPowerConsumption = Math.Max(currPowerConsumption - deltaTime * maxRechargeSpeed * RechargeAdjustSpeed, targetRechargeSpeed);
|
||||
}
|
||||
Charge += currPowerConsumption * Math.Min(Voltage, 1.0f) / 3600.0f * efficiency;
|
||||
}
|
||||
|
||||
if (charge <= 0.0f)
|
||||
{
|
||||
CurrPowerOutput = 0.0f;
|
||||
charge = 0.0f;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//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;
|
||||
loadReading = powerOut.Grid.Load;
|
||||
}
|
||||
|
||||
item.SendSignal(((int)Math.Round(-CurrPowerOutput)).ToString(), "power_value_out");
|
||||
item.SendSignal(((int)Math.Round(loadReading)).ToString(), "load_value_out");
|
||||
item.SendSignal(((int)Math.Round(Charge)).ToString(), "charge");
|
||||
item.SendSignal(((int)Math.Round(Charge / capacity * 100)).ToString(), "charge_%");
|
||||
item.SendSignal(((int)Math.Round(RechargeSpeed / maxRechargeSpeed * 100)).ToString(), "charge_rate");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the power consumption if checking the powerIn connection, or a negative value if the output can provide power when checking powerOut.
|
||||
/// Power consumption is proportional to set recharge speed and if there is less than max charge.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
if (connection == powerIn)
|
||||
{
|
||||
//Don't draw power if fully charged
|
||||
if (charge >= capacity)
|
||||
{
|
||||
charge = capacity;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float missingCharge = capacity - charge;
|
||||
float targetRechargeSpeed = rechargeSpeed;
|
||||
|
||||
if (ExponentialRechargeSpeed)
|
||||
{
|
||||
targetRechargeSpeed = MathF.Pow(rechargeSpeed / maxRechargeSpeed, 2) * maxRechargeSpeed;
|
||||
}
|
||||
//For the last kwMin scale the recharge rate linearly to prevent overcharging and to have a smooth cutoff
|
||||
if (missingCharge < 1.0f)
|
||||
{
|
||||
targetRechargeSpeed *= missingCharge;
|
||||
}
|
||||
|
||||
return MathHelper.Clamp(targetRechargeSpeed, 0, MaxRechargeSpeed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrPowerOutput = 0;
|
||||
return charge > 0 ? -1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimum and maximum output for the queried connection.
|
||||
/// Powerin min max equals CurrPowerConsumption as its abnormal for there to be power out.
|
||||
/// PowerOut min power out is zero and max is the maxout unless below 10% charge where
|
||||
/// the output is scaled relative to the 10% charge.
|
||||
/// </summary>
|
||||
/// <param name="connection">Connection being queried</param>
|
||||
/// <param name="load">Current grid load</param>
|
||||
/// <returns>Minimum and maximum power output for the connection</returns>
|
||||
public override PowerRange MinMaxPowerOut(Connection connection, float load = 0)
|
||||
{
|
||||
if (connection == powerOut)
|
||||
{
|
||||
float maxOutput;
|
||||
float chargeRatio = charge / capacity;
|
||||
if (chargeRatio < 0.1f)
|
||||
{
|
||||
maxOutput = Math.Max(chargeRatio * 10.0f, 0.0f) * MaxOutPut;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxOutput = MaxOutPut;
|
||||
}
|
||||
|
||||
//Limit max power out to not exceed the charge of the container
|
||||
maxOutput = Math.Min(maxOutput, charge * 60 / UpdateInterval);
|
||||
return new PowerRange(0.0f, maxOutput);
|
||||
}
|
||||
|
||||
return PowerRange.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalized power out from the container for the connection, provided the given grid information
|
||||
/// Output power based on the maxpower all batteries can output. So all batteries can
|
||||
/// equally share powerout based on their output capabilities.
|
||||
/// </summary>
|
||||
/// <param name="connection"></param>
|
||||
/// <param name="power"></param>
|
||||
/// <param name="minMaxPower"></param>
|
||||
/// <param name="load"></param>
|
||||
/// <returns></returns>
|
||||
public override float GetConnectionPowerOut(Connection connection, float power, PowerRange minMaxPower, float load)
|
||||
{
|
||||
if (connection == powerOut)
|
||||
{
|
||||
//Calculate the max power the container can output
|
||||
float maxPowerOutput = MaxOutPut;
|
||||
float chargeRatio = charge / capacity;
|
||||
if (chargeRatio < 0.1f)
|
||||
{
|
||||
maxPowerOutput *= Math.Max(chargeRatio * 10.0f, 0.0f);
|
||||
}
|
||||
|
||||
//Set power output based on the relative max power output capabilities and load demand
|
||||
CurrPowerOutput = MathHelper.Clamp((load - power) / minMaxPower.Max, 0, 1) * maxPowerOutput;
|
||||
return CurrPowerOutput;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the corresponding grid connection is resolved, adjust the container's charge.
|
||||
/// </summary>
|
||||
public override void GridResolved(Connection conn)
|
||||
{
|
||||
if (conn == powerIn)
|
||||
{
|
||||
//Increase charge based on how much power came in from the grid
|
||||
Charge += (CurrPowerConsumption * Voltage) / 60 * UpdateInterval * efficiency;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Decrease charge based on how much power is leaving the device
|
||||
Charge = Math.Clamp(Charge - CurrPowerOutput / 60 * UpdateInterval, 0, Capacity);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
|
||||
@@ -250,8 +305,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (HasBeenTuned) { return true; }
|
||||
|
||||
float targetRatio = string.IsNullOrEmpty(objective.Option) || objective.Option.Equals("charge", StringComparison.OrdinalIgnoreCase) ? aiRechargeTargetRatio : -1;
|
||||
if (targetRatio > 0 || float.TryParse(objective.Option, out targetRatio))
|
||||
float targetRatio = objective.Option.IsEmpty || objective.Option == "charge" ? aiRechargeTargetRatio : -1;
|
||||
if (targetRatio > 0 || float.TryParse(objective.Option.Value, out targetRatio))
|
||||
{
|
||||
if (Math.Abs(rechargeSpeed - maxRechargeSpeed * targetRatio) > 0.05f)
|
||||
{
|
||||
@@ -267,9 +322,10 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
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);
|
||||
character.Speak(TextManager.GetWithVariables("DialogChargeBatteries",
|
||||
("[itemname]", item.Name, FormatCapitals.Yes),
|
||||
("[rate]", ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString(), FormatCapitals.No)).Value,
|
||||
null, 1.0f, "chargebattery".ToIdentifier(), 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,9 +345,10 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
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);
|
||||
character.Speak(TextManager.GetWithVariables("DialogStopChargingBatteries",
|
||||
("[itemname]", item.Name, FormatCapitals.Yes),
|
||||
("[rate]", ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString(), FormatCapitals.No)).Value,
|
||||
null, 1.0f, "chargebattery".ToIdentifier(), 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,18 +26,25 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public float PowerLoad
|
||||
{
|
||||
get { return powerLoad; }
|
||||
get
|
||||
{
|
||||
if (this is RelayComponent || PowerConnections.Count == 0 || PowerConnections[0].Grid == null)
|
||||
{
|
||||
return powerLoad;
|
||||
}
|
||||
return PowerConnections[0].Grid.Load;
|
||||
}
|
||||
set { powerLoad = value; }
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the item be damaged if too much power is supplied to the power grid.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, 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:
|
||||
[Editable(MinValueFloat = 1.0f), Serialize(2.0f, IsPropertySaveable.Yes, 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
|
||||
@@ -46,14 +53,14 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.15f, true, description: "The probability for a fire to start when the item breaks."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
|
||||
[Serialize(0.15f, IsPropertySaveable.Yes, 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).")]
|
||||
[Serialize(false, IsPropertySaveable.No, 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;
|
||||
@@ -62,6 +69,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float extraLoad;
|
||||
private float extraLoadSetTime;
|
||||
|
||||
/// <summary>
|
||||
/// Additional load coming from somewhere else than the devices connected to the junction box (e.g. ballast flora or piezo crystals).
|
||||
/// Goes back to zero automatically if you stop setting the value.
|
||||
@@ -71,7 +79,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return extraLoad; }
|
||||
set
|
||||
{
|
||||
extraLoad = Math.Max(value, 0.0f);
|
||||
extraLoad = value;
|
||||
extraLoadSetTime = (float)Timing.TotalTime;
|
||||
}
|
||||
}
|
||||
@@ -112,7 +120,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public PowerTransfer(Item item, XElement element)
|
||||
public PowerTransfer(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
@@ -168,9 +176,34 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
RefreshConnections();
|
||||
|
||||
float powerReadingOut = 0;
|
||||
float loadReadingOut = ExtraLoad;
|
||||
if (powerLoad < 0)
|
||||
{
|
||||
powerReadingOut = -powerLoad;
|
||||
loadReadingOut = 0;
|
||||
}
|
||||
|
||||
if (powerOut != null && powerOut.Grid != null)
|
||||
{
|
||||
powerReadingOut = powerOut.Grid.Power;
|
||||
loadReadingOut = powerOut.Grid.Load;
|
||||
}
|
||||
|
||||
item.SendSignal(((int)Math.Round(powerReadingOut)).ToString(), "power_value_out");
|
||||
item.SendSignal(((int)Math.Round(loadReadingOut)).ToString(), "load_value_out");
|
||||
|
||||
if (Timing.TotalTime > extraLoadSetTime + 1.0)
|
||||
{
|
||||
extraLoad = Math.Max(extraLoad - 1000.0f * deltaTime, 0);
|
||||
//Decay the extra load to 0 from either positive or negative
|
||||
if (extraLoad > 0)
|
||||
{
|
||||
extraLoad = Math.Max(extraLoad - 1000.0f * deltaTime, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
extraLoad = Math.Min(extraLoad + 1000.0f * deltaTime, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!CanTransfer) { return; }
|
||||
@@ -200,7 +233,9 @@ namespace Barotrauma.Items.Components
|
||||
item.SendSignal(loadSignal, "load_value_out");
|
||||
|
||||
float maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
|
||||
Overload = -currPowerConsumption > Math.Max(powerLoad, 200.0f) * maxOverVoltage;
|
||||
|
||||
Overload = Voltage > maxOverVoltage;
|
||||
|
||||
if (Overload && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
|
||||
{
|
||||
if (overloadCooldownTimer > 0.0f)
|
||||
@@ -239,6 +274,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetConnectionPowerOut(Connection conn, float power, PowerRange minMaxPower, float load)
|
||||
{
|
||||
return conn == powerOut ? PowerConsumption + ExtraLoad : 0;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return picker != null;
|
||||
@@ -376,25 +416,6 @@ namespace Barotrauma.Items.Components
|
||||
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(Signal signal, Connection connection)
|
||||
{
|
||||
if (item.Condition <= 0.0f || connection.IsPower) { return; }
|
||||
|
||||
@@ -8,10 +8,57 @@ using Barotrauma.Sounds;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Order in which power sources will provide to a grid, lower number is higher priority
|
||||
/// </summary>
|
||||
public enum PowerPriority
|
||||
{
|
||||
Default = 0, // Use for status effects and/or extraload
|
||||
Reactor = 1,
|
||||
Relay = 3,
|
||||
Battery = 5
|
||||
}
|
||||
|
||||
readonly struct PowerRange
|
||||
{
|
||||
public readonly static PowerRange Zero = default;
|
||||
|
||||
public readonly float Min;
|
||||
public readonly float Max;
|
||||
|
||||
/// <summary>
|
||||
/// Used by reactors to communicate their maximum output to each other so they can divide the grid load between each other in a sensible way
|
||||
/// </summary>
|
||||
public readonly float ReactorMaxOutput;
|
||||
|
||||
public PowerRange(float min, float max) : this(min, max, 0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
public PowerRange(float min, float max, float reactorMaxOutput)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(max >= min);
|
||||
System.Diagnostics.Debug.Assert(min >= 0);
|
||||
System.Diagnostics.Debug.Assert(max >= 0);
|
||||
Min = min;
|
||||
Max = max;
|
||||
ReactorMaxOutput = reactorMaxOutput;
|
||||
}
|
||||
|
||||
public static PowerRange operator +(PowerRange a, PowerRange b)
|
||||
{
|
||||
return new PowerRange(a.Min + b.Min, a.Max + b.Max, a.ReactorMaxOutput + b.ReactorMaxOutput);
|
||||
}
|
||||
public static PowerRange operator -(PowerRange a, PowerRange b)
|
||||
{
|
||||
return new PowerRange(a.Min - b.Min, a.Max - b.Max, a.ReactorMaxOutput - b.ReactorMaxOutput);
|
||||
}
|
||||
}
|
||||
|
||||
partial class Powered : ItemComponent
|
||||
{
|
||||
private static float updateTimer;
|
||||
protected static float UpdateInterval = 0.2f;
|
||||
//TODO: test sparser update intervals?
|
||||
protected const float UpdateInterval = (float)Timing.Step;
|
||||
|
||||
/// <summary>
|
||||
/// List of all powered ItemComponents
|
||||
@@ -22,10 +69,9 @@ namespace Barotrauma.Items.Components
|
||||
get { return poweredList; }
|
||||
}
|
||||
|
||||
/// <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>();
|
||||
public static readonly List<Connection> ChangedConnections = new List<Connection>();
|
||||
|
||||
public readonly static Dictionary<int, GridInfo> Grids = new Dictionary<int, GridInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// The amount of power currently consumed by the item. Negative values mean that the item is providing power to connected items
|
||||
@@ -49,7 +95,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
protected Connection powerIn, powerOut;
|
||||
|
||||
[Editable, Serialize(0.5f, true, description: "The minimum voltage required for the device to function. " +
|
||||
protected virtual PowerPriority Priority { get { return PowerPriority.Default; } }
|
||||
|
||||
[Editable, Serialize(0.5f, IsPropertySaveable.Yes, 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
|
||||
@@ -58,14 +106,14 @@ namespace Barotrauma.Items.Components
|
||||
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.")]
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, 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.")]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Is the device currently active. Inactive devices don't consume power.")]
|
||||
public override bool IsActive
|
||||
{
|
||||
get { return base.IsActive; }
|
||||
@@ -79,35 +127,63 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[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).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, 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).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, 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); }
|
||||
get
|
||||
{
|
||||
if (powerIn != null)
|
||||
{
|
||||
if (powerIn?.Grid != null) { return powerIn.Grid.Voltage; }
|
||||
}
|
||||
else if (powerOut != null)
|
||||
{
|
||||
if (powerOut?.Grid != null) { return powerOut.Grid.Voltage; }
|
||||
}
|
||||
return voltage;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (powerIn != null)
|
||||
{
|
||||
if (powerIn.Grid != null)
|
||||
{
|
||||
powerIn.Grid.Voltage = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
else if (powerOut != null)
|
||||
{
|
||||
if (powerOut.Grid != null)
|
||||
{
|
||||
powerOut.Grid.Voltage = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
voltage = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the item be damaged by electomagnetic pulses.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the item be damaged by electomagnetic pulses.")]
|
||||
public bool VulnerableToEMP
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Powered(Item item, XElement element)
|
||||
public Powered(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
poweredList.Add(this);
|
||||
InitProjectSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjectSpecific(XElement element);
|
||||
partial void InitProjectSpecific(ContentXElement element);
|
||||
|
||||
protected void UpdateOnActiveEffects(float deltaTime)
|
||||
{
|
||||
@@ -115,19 +191,19 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
//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)
|
||||
if (PowerConsumption <= 0.0f)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (voltage > minVoltage)
|
||||
if (Voltage > minVoltage)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
#if CLIENT
|
||||
if (voltage > minVoltage)
|
||||
if (Voltage > minVoltage)
|
||||
{
|
||||
if (!powerOnSoundPlayed && powerOnSound != null)
|
||||
{
|
||||
@@ -135,7 +211,7 @@ namespace Barotrauma.Items.Components
|
||||
powerOnSoundPlayed = true;
|
||||
}
|
||||
}
|
||||
else if (voltage < 0.1f)
|
||||
else if (Voltage < 0.1f)
|
||||
{
|
||||
powerOnSoundPlayed = false;
|
||||
}
|
||||
@@ -144,7 +220,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
currPowerConsumption = powerConsumption;
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
}
|
||||
|
||||
@@ -163,6 +238,7 @@ namespace Barotrauma.Items.Components
|
||||
else if (c.Name == "power_out")
|
||||
{
|
||||
powerOut = c;
|
||||
powerOut.Priority = Priority;
|
||||
}
|
||||
else if (c.Name == "power")
|
||||
{
|
||||
@@ -182,6 +258,7 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
powerOut = c;
|
||||
powerOut.Priority = Priority;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -199,104 +276,388 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ReceivePowerProbeSignal(Connection connection, Item source, float power) { }
|
||||
|
||||
/// <summary>
|
||||
/// Allocate electrical devices into their grids based on connections
|
||||
/// </summary>
|
||||
/// <param name="useCache">Use previous grids and change in connections</param>
|
||||
public static void UpdateGrids(bool useCache = true)
|
||||
{
|
||||
//don't use cache if there are no existing grids
|
||||
if (Grids.Count > 0 && useCache)
|
||||
{
|
||||
//delete all grids that were affected
|
||||
foreach (Connection c in ChangedConnections)
|
||||
{
|
||||
if (c.Grid != null)
|
||||
{
|
||||
Grids.Remove(c.Grid.ID);
|
||||
c.Grid = null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Connection c in ChangedConnections)
|
||||
{
|
||||
//Make sure the connection grid hasn't been resolved by another connection update
|
||||
//Ensure the connection has other connections
|
||||
if (c.Grid == null && c.Recipients.Count > 0 && c.Item.Condition > 0.0f)
|
||||
{
|
||||
GridInfo grid = PropagateGrid(c);
|
||||
Grids[grid.ID] = grid;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Clear all grid IDs from connections
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
//Only check devices with connectors
|
||||
if (powered.powerIn != null)
|
||||
{
|
||||
powered.powerIn.Grid = null;
|
||||
}
|
||||
if (powered.powerOut != null)
|
||||
{
|
||||
powered.powerOut.Grid = null;
|
||||
}
|
||||
}
|
||||
|
||||
Grids.Clear();
|
||||
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
//Probe through all connections that don't have a gridID
|
||||
if (powered.powerIn != null && powered.powerIn.Grid == null && powered.powerIn != powered.powerOut && powered.Item.Condition > 0.0f)
|
||||
{
|
||||
// Only create grids for networks with more than 1 device
|
||||
if (powered.powerIn.Recipients.Count > 0)
|
||||
{
|
||||
GridInfo grid = PropagateGrid(powered.powerIn);
|
||||
Grids[grid.ID] = grid;
|
||||
}
|
||||
}
|
||||
|
||||
if (powered.powerOut != null && powered.powerOut.Grid == null && powered.Item.Condition > 0.0f)
|
||||
{
|
||||
//Only create grids for networks with more than 1 device
|
||||
if (powered.powerOut.Recipients.Count > 0)
|
||||
{
|
||||
GridInfo grid = PropagateGrid(powered.powerOut);
|
||||
Grids[grid.ID] = grid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Clear changed connections after each update
|
||||
ChangedConnections.Clear();
|
||||
}
|
||||
|
||||
private static GridInfo PropagateGrid(Connection conn)
|
||||
{
|
||||
//Generate unique Key
|
||||
int id = Rand.Int(int.MaxValue, Rand.RandSync.Unsynced);
|
||||
while (Grids.ContainsKey(id))
|
||||
{
|
||||
id = Rand.Int(int.MaxValue, Rand.RandSync.Unsynced);
|
||||
}
|
||||
|
||||
return PropagateGrid(conn, id);
|
||||
}
|
||||
|
||||
private static GridInfo PropagateGrid(Connection conn, int gridID)
|
||||
{
|
||||
Stack<Connection> probeStack = new Stack<Connection>();
|
||||
|
||||
GridInfo grid = new GridInfo(gridID);
|
||||
|
||||
probeStack.Push(conn);
|
||||
|
||||
//Non recursive approach to traversing connection tree
|
||||
while (probeStack.Count > 0)
|
||||
{
|
||||
Connection c = probeStack.Pop();
|
||||
c.Grid = grid;
|
||||
grid.AddConnection(c);
|
||||
|
||||
//Add on recipients
|
||||
foreach (Connection otherC in c.Recipients)
|
||||
{
|
||||
//Only add valid connections
|
||||
if (otherC.Grid != grid && (otherC.Grid == null || !Grids.ContainsKey(otherC.Grid.ID)) && ValidPowerConnection(c, otherC))
|
||||
{
|
||||
if (otherC.Item.Condition <= 0.0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
otherC.Grid = grid; //Assigning ID early prevents unncessary adding to stack
|
||||
probeStack.Push(otherC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the power calculations of all devices and grids
|
||||
/// Updates grids in the order of
|
||||
/// ConnCurrConsumption - Get load of device/ flag it as an outputting connection
|
||||
/// -- If outputting power --
|
||||
/// MinMaxPower - Minimum and Maximum power output of the connection for devices to coordinate
|
||||
/// ConnPowerOut - Final power output based on the sum of the MinMaxPower
|
||||
/// -- Finally --
|
||||
/// GridResolved - Indicate that a connection's grid has been finished being calculated
|
||||
///
|
||||
/// Power outputting devices are calculated in stages based on their priority
|
||||
/// Reactors will output first, followed by relays then batteries.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="deltaTime"></param>
|
||||
public static void UpdatePower(float deltaTime)
|
||||
{
|
||||
//Don't update the power if the round is ending
|
||||
if (GameMain.GameSession != null && GameMain.GameSession.RoundEnding)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Only update the power at the given update interval
|
||||
/*
|
||||
//Not use currently as update interval of 1/60
|
||||
if (updateTimer > 0.0f)
|
||||
{
|
||||
updateTimer -= deltaTime;
|
||||
return;
|
||||
}
|
||||
updateTimer = UpdateInterval;
|
||||
*/
|
||||
|
||||
//reset power first
|
||||
foreach (Powered powered in poweredList)
|
||||
#if CLIENT
|
||||
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
|
||||
sw.Start();
|
||||
#endif
|
||||
//Ensure all grids are updated correctly and have the correct connections
|
||||
UpdateGrids();
|
||||
|
||||
#if CLIENT
|
||||
sw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("GridUpdate", sw.ElapsedTicks);
|
||||
sw.Restart();
|
||||
#endif
|
||||
|
||||
//Reset all grids
|
||||
foreach (GridInfo grid in Grids.Values)
|
||||
{
|
||||
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; }
|
||||
//Wipe priority groups as connections can change to not be outputting -- Can be improved caching wise --
|
||||
grid.PowerSourceGroups.Clear();
|
||||
grid.Power = 0;
|
||||
grid.Load = 0;
|
||||
}
|
||||
|
||||
//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
|
||||
//Determine if devices are adding a load or providing power, also resolve solo nodes
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer pt)
|
||||
//Handle the device if it's got a power connection
|
||||
if (powered.powerIn != null && powered.powerOut != powered.powerIn)
|
||||
{
|
||||
if (pt.ExtraLoad > 0.0f)
|
||||
{
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerIn?.SendPowerProbeSignal(powered.item, -pt.ExtraLoad);
|
||||
//Get the new load for the connection
|
||||
float currLoad;
|
||||
if (powered.Item.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering && repairable.TinkeringPowersDevices && !(powered is PowerContainer))
|
||||
{
|
||||
currLoad = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
currLoad = powered.GetCurrentPowerConsumption(powered.powerIn);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else 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 || pc.item.Condition <= 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.Item.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering && repairable.TinkeringPowersDevices)) && !(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)
|
||||
//If its a load update its grid load
|
||||
if (currLoad >= 0)
|
||||
{
|
||||
float voltage = -pt.CurrPowerConsumption / Math.Max(pt.PowerLoad, 1.0f);
|
||||
powered.voltage = Math.Max(powered.voltage, voltage);
|
||||
continue;
|
||||
powered.CurrPowerConsumption = currLoad;
|
||||
if (powered.powerIn.Grid != null)
|
||||
{
|
||||
powered.powerIn.Grid.Load += currLoad;
|
||||
}
|
||||
}
|
||||
var pc = powerSource.Item.GetComponent<PowerContainer>();
|
||||
if (pc != null && pc.item.Condition > 0.0f)
|
||||
else if (powered.powerIn.Grid != null)
|
||||
{
|
||||
float voltage = pc.CurrPowerOutput / Math.Max(powered.CurrPowerConsumption, 1.0f);
|
||||
powered.voltage += voltage;
|
||||
//If connected to a grid add as a source to be processed
|
||||
powered.powerIn.Grid.AddSrc(powered.powerIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
powered.CurrPowerConsumption = powered.GetConnectionPowerOut(powered.powerIn, 0, powered.MinMaxPowerOut(powered.powerIn, 0), 0);
|
||||
powered.GridResolved(powered.powerIn);
|
||||
}
|
||||
}
|
||||
|
||||
//Handle the device power depending on if its powerout
|
||||
if (powered.powerOut != null)
|
||||
{
|
||||
//Get the connection's load
|
||||
float currLoad = powered.GetCurrentPowerConsumption(powered.powerOut);
|
||||
|
||||
//Update the device's output load to the correct variable
|
||||
if (powered is PowerTransfer pt)
|
||||
{
|
||||
pt.PowerLoad = currLoad;
|
||||
}
|
||||
else if (powered is PowerContainer pc)
|
||||
{
|
||||
// PowerContainer handle its own output value
|
||||
}
|
||||
else
|
||||
{
|
||||
powered.CurrPowerConsumption = currLoad;
|
||||
}
|
||||
|
||||
if (currLoad >= 0)
|
||||
{
|
||||
//Add to the grid load if possible
|
||||
if (powered.powerOut.Grid != null)
|
||||
{
|
||||
powered.powerOut.Grid.Load += currLoad;
|
||||
}
|
||||
}
|
||||
else if (powered.powerOut.Grid != null)
|
||||
{
|
||||
//Add connection as a source to be processed
|
||||
powered.powerOut.Grid.AddSrc(powered.powerOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Perform power calculations for the singular connection
|
||||
float loadOut = powered.GetConnectionPowerOut(powered.powerOut, 0, powered.MinMaxPowerOut(powered.powerOut, 0), 0);
|
||||
if (powered is PowerTransfer pt2)
|
||||
{
|
||||
pt2.PowerLoad = loadOut;
|
||||
}
|
||||
else if (powered is PowerContainer pc)
|
||||
{
|
||||
//PowerContainer handles its own output value
|
||||
}
|
||||
else
|
||||
{
|
||||
powered.CurrPowerConsumption = loadOut;
|
||||
}
|
||||
|
||||
//Indicate grid is resolved as it was the only device
|
||||
powered.GridResolved(powered.powerOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Iterate through all grids to determine the power on the grid
|
||||
foreach (GridInfo grid in Grids.Values)
|
||||
{
|
||||
//Iterate through the priority src groups lowest first
|
||||
foreach (PowerSourceGroup scrGroup in grid.PowerSourceGroups.Values)
|
||||
{
|
||||
scrGroup.MinMaxPower = PowerRange.Zero;
|
||||
|
||||
//Iterate through all connections in the group to get their minmax power and sum them
|
||||
foreach (Connection c in scrGroup.Connections)
|
||||
{
|
||||
Powered device = c.Item.GetComponent<Powered>();
|
||||
scrGroup.MinMaxPower += device.MinMaxPowerOut(c, grid.Load);
|
||||
}
|
||||
|
||||
//Iterate through all connections to get their final power out provided the min max information
|
||||
float addedPower = 0;
|
||||
foreach (Connection c in scrGroup.Connections)
|
||||
{
|
||||
Powered device = c.Item.GetComponent<Powered>();
|
||||
addedPower += device.GetConnectionPowerOut(c, grid.Power, scrGroup.MinMaxPower, grid.Load);
|
||||
}
|
||||
|
||||
//Add the power to the grid
|
||||
grid.Power += addedPower;
|
||||
}
|
||||
|
||||
//Calculate Grid voltage, limit between 0 - 1000
|
||||
float newVoltage = MathHelper.Min(grid.Power / MathHelper.Max(grid.Load, 1E-10f), 1000);
|
||||
if (float.IsNegative(newVoltage))
|
||||
{
|
||||
newVoltage = 0.0f;
|
||||
}
|
||||
|
||||
grid.Voltage = newVoltage;
|
||||
|
||||
//Iterate through all connections on that grid and run their gridResolved function
|
||||
foreach (Connection con in grid.Connections)
|
||||
{
|
||||
Powered device = con.Item.GetComponent<Powered>();
|
||||
device.GridResolved(con);
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
sw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("PowerUpdate", sw.ElapsedTicks);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current power consumption of the device (or amount of generated power if negative)
|
||||
/// </summary>
|
||||
/// <param name="connection">Connection to calculate power consumption for.</param>
|
||||
public virtual float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
// If a handheld device there is no consumption
|
||||
if (powerIn == null && powerOut == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add extraload for PowerTransfer devices
|
||||
if (this is PowerTransfer pt)
|
||||
{
|
||||
return PowerConsumption + pt.ExtraLoad;
|
||||
}
|
||||
else if (connection != this.powerIn || !IsActive)
|
||||
{
|
||||
//If not the power in connection or is inactive there is no draw
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Otherwise return the max powerconsumption of the device
|
||||
return PowerConsumption;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimum and maximum power the connection can provide
|
||||
/// </summary>
|
||||
/// <param name="conn">Connection being queried about its power capabilities</param>
|
||||
/// <param name="load">Load of the connected grid</param>
|
||||
public virtual PowerRange MinMaxPowerOut(Connection conn, float load = 0)
|
||||
{
|
||||
return PowerRange.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalize how much power the device will be outputting to the connection
|
||||
/// </summary>
|
||||
/// <param name="conn">Connection being queried</param>
|
||||
/// <param name="power">Current grid power</param>
|
||||
/// <param name="load">Current load on the grid</param>
|
||||
/// <returns>Power pushed to the grid</returns>
|
||||
public virtual float GetConnectionPowerOut(Connection conn, float power, PowerRange minMaxPower, float load)
|
||||
{
|
||||
return conn == powerOut ? MathHelper.Max(-CurrPowerConsumption, 0) : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be overridden to perform updates for the device after the connected grid has resolved its power calculations, i.e. storing voltage for later updates
|
||||
/// </summary>
|
||||
public virtual void GridResolved(Connection conn) { }
|
||||
|
||||
public static bool ValidPowerConnection(Connection conn1, Connection conn2)
|
||||
{
|
||||
return conn1.IsPower && conn2.IsPower && (conn1.Item.HasTag("junctionbox") || conn2.Item.HasTag("junctionbox") || conn1.IsOutput != conn2.IsOutput || (conn1.Item.HasTag("dock") && conn2.Item.HasTag("dock")));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -314,7 +675,6 @@ namespace Barotrauma.Items.Components
|
||||
if (!recipient.IsPower || !recipient.IsOutput) { continue; }
|
||||
var battery = recipient.Item?.GetComponent<PowerContainer>();
|
||||
if (battery == null) { continue; }
|
||||
|
||||
float maxOutputPerFrame = battery.MaxOutPut / 60.0f;
|
||||
float framesPerMinute = 3600.0f;
|
||||
availablePower += Math.Min(battery.Charge * framesPerMinute, maxOutputPerFrame);
|
||||
@@ -323,10 +683,119 @@ namespace Barotrauma.Items.Components
|
||||
return availablePower;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Efficient method to retrieve the batteries connected to the device
|
||||
/// </summary>
|
||||
/// <returns>All connected PowerContainers</returns>
|
||||
protected List<PowerContainer> GetConnectedBatteries(bool outputOnly = true)
|
||||
{
|
||||
List<PowerContainer> batteries = new List<PowerContainer>();
|
||||
GridInfo supplyingGrid = null;
|
||||
|
||||
//Determine supplying grid, prefer PowerIn connection
|
||||
if (powerIn != null)
|
||||
{
|
||||
if (powerIn.Grid != null)
|
||||
{
|
||||
supplyingGrid = powerIn.Grid;
|
||||
}
|
||||
}
|
||||
else if (powerOut != null)
|
||||
{
|
||||
if (powerOut.Grid != null)
|
||||
{
|
||||
supplyingGrid = powerOut.Grid;
|
||||
}
|
||||
}
|
||||
|
||||
if (supplyingGrid != null)
|
||||
{
|
||||
//Iterate through all connections to fine powerContainers
|
||||
foreach (Connection c in supplyingGrid.Connections)
|
||||
{
|
||||
PowerContainer pc = c.Item.GetComponent<PowerContainer>();
|
||||
if (pc != null && (!outputOnly || pc.powerOut == c))
|
||||
{
|
||||
batteries.Add(pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return batteries;
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
//Flag power connections to be updated
|
||||
if (item.Connections != null)
|
||||
{
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
if (c.IsPower && c.Grid != null)
|
||||
{
|
||||
ChangedConnections.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.RemoveComponentSpecific();
|
||||
poweredList.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
partial class GridInfo
|
||||
{
|
||||
public readonly int ID;
|
||||
public float Voltage = 0;
|
||||
public float Load = 0;
|
||||
public float Power = 0;
|
||||
|
||||
public readonly List<Connection> Connections = new List<Connection>();
|
||||
public readonly SortedList<PowerPriority, PowerSourceGroup> PowerSourceGroups = new SortedList<PowerPriority, PowerSourceGroup>();
|
||||
|
||||
public GridInfo(int id)
|
||||
{
|
||||
ID = id;
|
||||
}
|
||||
|
||||
public void RemoveConnection(Connection c)
|
||||
{
|
||||
Connections.Remove(c);
|
||||
|
||||
//Remove the grid if it has no devices
|
||||
if (Connections.Count == 0 && Powered.Grids.ContainsKey(ID))
|
||||
{
|
||||
Powered.Grids.Remove(ID);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddConnection(Connection c)
|
||||
{
|
||||
Connections.Add(c);
|
||||
}
|
||||
|
||||
public void AddSrc(Connection c)
|
||||
{
|
||||
if (PowerSourceGroups.ContainsKey(c.Priority))
|
||||
{
|
||||
PowerSourceGroups[c.Priority].Connections.Add(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
PowerSourceGroup group = new PowerSourceGroup();
|
||||
group.Connections.Add(c);
|
||||
PowerSourceGroups[c.Priority] = group;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial class PowerSourceGroup
|
||||
{
|
||||
public PowerRange MinMaxPower;
|
||||
public readonly List<Connection> Connections = new List<Connection>();
|
||||
|
||||
public PowerSourceGroup()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user