using Barotrauma.Networking; using Lidgren.Network; 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, outputVoltage; //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; //a list of powered devices connected directly to this item private readonly List> directlyConnected = new List>(10); public float CurrPowerOutput { get; private set; } [Serialize("0,0", true)] public Vector2 IndicatorPosition { get { return indicatorPosition; } set { indicatorPosition = value; } } [Serialize("0,0", true)] public Vector2 IndicatorSize { get { return indicatorSize; } set { indicatorSize = value; } } [Serialize(false, true)] public bool IsHorizontal { get { return isHorizontal; } set { isHorizontal = value; } } [Editable(ToolTip = "Maximum output of the device when fully charged (kW)."), Serialize(10.0f, true)] public float MaxOutPut { set; get; } [Serialize(10.0f, true), Editable(ToolTip = "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)] 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); [Serialize(10.0f, true), Editable(ToolTip = "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); } } [Serialize(10.0f, true), Editable] 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)); } } 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) { float chargeRatio = charge / capacity; float gridPower = 0.0f; float gridLoad = 0.0f; directlyConnected.Clear(); foreach (Connection c in item.Connections) { if (c.Name == "power_in") continue; foreach (Connection c2 in c.Recipients) { if (c2.Item.Condition <= 0.0f) { continue; } PowerTransfer pt = c2.Item.GetComponent(); if (pt == null) { foreach (Powered powered in c2.Item.GetComponents()) { if (!powered.IsActive) continue; directlyConnected.Add(new Pair(powered, c2)); gridLoad += powered.CurrPowerConsumption; } continue; } if (!pt.IsActive || !pt.CanTransfer) { continue; } gridLoad += pt.PowerLoad; gridPower -= pt.CurrPowerConsumption; } } 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 * rechargeVoltage / 3600.0f; } //provide power to the grid if (gridLoad > 0.0f) { if (charge <= 0.0f) { CurrPowerOutput = 0.0f; charge = 0.0f; return; } if (gridPower < gridLoad) { //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 = MathHelper.Lerp( CurrPowerOutput, Math.Min(MaxOutPut * maxOutputRatio, gridLoad), deltaTime * 10.0f); } else { CurrPowerOutput = MathHelper.Lerp(CurrPowerOutput, 0.0f, deltaTime * 10.0f); } Charge -= CurrPowerOutput / 3600.0f; } item.SendSignal(0, ((int)Charge).ToString(), "charge", null); item.SendSignal(0, ((int)((Charge / capacity) * 100)).ToString(), "charge_%", null); item.SendSignal(0, ((int)((RechargeSpeed / maxRechargeSpeed) * 100)).ToString(), "charge_rate", null); foreach (Pair connected in directlyConnected) { connected.First.ReceiveSignal(0, "", connected.Second, source: item, sender: null, power: gridLoad <= 0.0f ? 1.0f : CurrPowerOutput / gridLoad); } rechargeVoltage = 0.0f; outputVoltage = 0.0f; } public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { #if CLIENT if (GameMain.Client != null) return false; #endif if (string.IsNullOrEmpty(objective.Option) || objective.Option.ToLowerInvariant() == "charge") { if (Math.Abs(rechargeSpeed - maxRechargeSpeed * 0.5f) > 0.05f) { #if SERVER item.CreateServerEvent(this); #endif RechargeSpeed = maxRechargeSpeed * 0.5f; #if CLIENT rechargeSpeedSlider.BarScroll = RechargeSpeed / Math.Max(maxRechargeSpeed, 1.0f); #endif character.Speak(TextManager.Get("DialogChargeBatteries") .Replace("[itemname]", item.Name) .Replace("[rate]", ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString()), null, 1.0f, "chargebattery", 10.0f); } } else { if (rechargeSpeed > 0.0f) { #if SERVER item.CreateServerEvent(this); #endif RechargeSpeed = 0.0f; #if CLIENT rechargeSpeedSlider.BarScroll = RechargeSpeed / Math.Max(maxRechargeSpeed, 1.0f); #endif character.Speak(TextManager.Get("DialogStopChargingBatteries") .Replace("[itemname]", item.Name) .Replace("[rate]", ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString()), 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.Name == "set_rate") { float tempSpeed; if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out tempSpeed)) { if (!MathUtils.IsValid(tempSpeed)) return; RechargeSpeed = MathHelper.Clamp(tempSpeed / 100.0f, 0.0f, 1.0f) * MaxRechargeSpeed; } } if (!connection.IsPower) return; if (connection.Name == "power_in") { rechargeVoltage = Math.Min(power, 1.0f); } else { outputVoltage = power; } } } }