using Content.Server.GameObjects.EntitySystems; using SS14.Server.GameObjects; using SS14.Shared.GameObjects; using SS14.Shared.Interfaces.GameObjects; using SS14.Shared.IoC; using SS14.Shared.Utility; using System; using System.Collections.Generic; using System.Linq; using YamlDotNet.RepresentationModel; namespace Content.Server.GameObjects.Components.Power { /// /// Component that requires power to function /// public class PowerDeviceComponent : Component, EntitySystems.IExamine { public override string Name => "PowerDevice"; /// /// The method of draw we will try to use to place our load set via component parameter, defaults to using power providers /// public virtual DrawTypes DrawType { get; protected set; } = DrawTypes.Provider; /// /// The power draw method we are currently connected to and using /// public DrawTypes Connected { get; protected set; } = DrawTypes.None; public bool Powered { get; private set; } = false; /// /// Is an external power source currently available? /// public bool ExternalPowered { get => _externalPowered; set { _externalPowered = value; UpdatePowered(); } } private bool _externalPowered = false; /// /// Is an internal power source currently available? /// public bool InternalPowered { get => _internalPowered; set { _internalPowered = value; UpdatePowered(); } } private bool _internalPowered = false; /// /// Priority for powernet draw, lower will draw first, defined in powernet.cs /// public virtual Powernet.Priority Priority { get; protected set; } = Powernet.Priority.Medium; private float _load = 100; //arbitrary magic number to start /// /// Power load from this entity. /// In Watts. /// public float Load { get => _load; set => UpdateLoad(value); } /// /// All the power providers that we are within range of /// public List AvailableProviders = new List(); private PowerProviderComponent _provider; /// /// A power provider that will handle our load, if we are linked to any /// public PowerProviderComponent Provider { get => _provider; set { Connected = DrawTypes.Provider; if (_provider != null) { _provider.RemoveDevice(this); } if (value != null) { _provider = value; _provider.AddDevice(this); } else { Connected = DrawTypes.None; } } } public event EventHandler OnPowerStateChanged; public override void OnAdd() { base.OnAdd(); if (DrawType == DrawTypes.Node || DrawType == DrawTypes.Both) { if (!Owner.TryGetComponent(out PowerNodeComponent node)) { Owner.AddComponent(); node = Owner.GetComponent(); } node.OnPowernetConnect += PowernetConnect; node.OnPowernetDisconnect += PowernetDisconnect; node.OnPowernetRegenerate += PowernetRegenerate; } } public override void Shutdown() { if (Owner.TryGetComponent(out PowerNodeComponent node)) { if (node.Parent != null) { node.Parent.RemoveDevice(this); } node.OnPowernetConnect -= PowernetConnect; node.OnPowernetDisconnect -= PowernetDisconnect; node.OnPowernetRegenerate -= PowernetRegenerate; } if (Provider != null) { Provider.RemoveDevice(this); } base.Shutdown(); } public override void LoadParameters(YamlMappingNode mapping) { if (mapping.TryGetNode("drawtype", out YamlNode node)) { DrawType = node.AsEnum(); } if (mapping.TryGetNode("load", out node)) { Load = node.AsFloat(); } if (mapping.TryGetNode("priority", out node)) { Priority = node.AsEnum(); } } string IExamine.Examine() { if (!Powered) { return "The device is not powered"; } return null; } private void UpdateLoad(float value) { var oldLoad = _load; _load = value; if (Connected == DrawTypes.Node) { var node = Owner.GetComponent(); node.Parent.UpdateDevice(this, oldLoad); } else if (Connected == DrawTypes.Provider) { Provider.UpdateDevice(this, oldLoad); } } private void UpdatePowered() { var oldPowered = Powered; Powered = ExternalPowered || InternalPowered; if (oldPowered != Powered) { if (Powered) { OnPowerStateChanged?.Invoke(this, new PowerStateEventArgs(true)); } else { OnPowerStateChanged?.Invoke(this, new PowerStateEventArgs(false)); } } } /// /// Register a new power provider as a possible connection to this device /// /// public void AddProvider(PowerProviderComponent provider) { AvailableProviders.Add(provider); if (Connected != DrawTypes.Node) { ConnectToBestProvider(); } } /// /// Find the nearest registered power provider and connect to it /// private void ConnectToBestProvider() { //Any values we can connect to or are we already connected to a node, cancel! if (!AvailableProviders.Any() || Connected == DrawTypes.Node) return; //Get the starting value for our loop var position = Owner.GetComponent().WorldPosition; var bestprovider = AvailableProviders[0]; //If we are already connected to a power provider we need to do a loop to find the nearest one, otherwise skip it and use first entry if (Connected == DrawTypes.Provider) { var bestdistance = (bestprovider.Owner.GetComponent().WorldPosition - position).LengthSquared; foreach (var availprovider in AvailableProviders) { //Find distance to new provider var distance = (availprovider.Owner.GetComponent().WorldPosition - position).LengthSquared; //If new provider distance is shorter it becomes new best possible provider if (distance < bestdistance) { bestdistance = distance; bestprovider = availprovider; } } } if (Provider != bestprovider) Provider = bestprovider; } /// /// Remove a power provider from being a possible connection to this device /// /// public void RemoveProvider(PowerProviderComponent provider) { if (!AvailableProviders.Contains(provider)) return; AvailableProviders.Remove(provider); if (provider == Provider) { Provider = null; ExternalPowered = false; } if (Connected != DrawTypes.Node) { ConnectToBestProvider(); } } /// /// Node has become anchored to a powernet /// /// /// protected virtual void PowernetConnect(object sender, PowernetEventArgs eventarg) { //This sets connected = none so it must be first Provider = null; eventarg.Powernet.AddDevice(this); Connected = DrawTypes.Node; } /// /// Powernet wire was remove so we need to regenerate the powernet /// /// /// protected virtual void PowernetRegenerate(object sender, PowernetEventArgs eventarg) { eventarg.Powernet.AddDevice(this); } /// /// Node has become unanchored from a powernet /// /// /// protected virtual void PowernetDisconnect(object sender, PowernetEventArgs eventarg) { eventarg.Powernet.RemoveDevice(this); Connected = DrawTypes.None; ConnectToBestProvider(); } internal virtual void ProcessInternalPower(float frametime) { if (Owner.TryGetComponent(out var storage) && storage.CanDeductCharge(Load)) { // We still keep InternalPowered correct if connected externally, // but don't use it. if (!ExternalPowered) { storage.DeductCharge(Load); } InternalPowered = true; } else { InternalPowered = false; } } } public enum DrawTypes { None = 0, Node = 1, Provider = 2, Both = 3, } public class PowerStateEventArgs : EventArgs { public readonly bool Powered; public PowerStateEventArgs(bool powered) { Powered = powered; } } }