using SS14.Server.GameObjects; using SS14.Shared.GameObjects; using SS14.Shared.Interfaces.GameObjects; using SS14.Shared.IoC; using SS14.Shared.Utility; 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 { 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 = false; /// /// Status indicator variable for powered /// public virtual bool Powered { get => _powered; set => SetPowered(value); } /// /// 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 /// 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 override void OnAdd(IEntity owner) { base.OnAdd(owner); if (Drawtype == DrawTypes.Both || Drawtype == DrawTypes.Node) { if (!owner.TryGetComponent(out PowerNodeComponent node)) { var factory = IoCManager.Resolve(); node = factory.GetComponent(); owner.AddComponent(node); } node.OnPowernetConnect += PowernetConnect; node.OnPowernetDisconnect += PowernetDisconnect; node.OnPowernetRegenerate += PowernetRegenerate; } } public override void OnRemove() { if (Owner.TryGetComponent(out PowerNodeComponent node)) { if(node.Parent != null) { node.Parent.RemoveDevice(this); } node.OnPowernetConnect -= PowernetConnect; node.OnPowernetDisconnect -= PowernetDisconnect; node.OnPowernetRegenerate -= PowernetRegenerate; } base.OnRemove(); } 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(); } } 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); } } /// /// Changes behavior when receiving a command to become powered or depowered /// /// public virtual void SetPowered(bool value) { //Let them set us to true if (value == true) { _powered = true; return; } //A powernet has decided we will not be powered this tick, lets try to power ourselves if (value == false && Owner.TryGetComponent(out PowerStorageComponent storage)) { if (storage.CanDeductCharge(Load)) { storage.DeductCharge(Load); _powered = true; return; } } //For some reason above we could not power ourselves, we depower _powered = false; return; } /// /// 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 (Connected != DrawTypes.Node) { ConnectToBestProvider(); } } /// /// Node has become anchored to a powernet /// /// /// private 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 /// /// /// private void PowernetRegenerate(object sender, PowernetEventArgs eventarg) { eventarg.Powernet.AddDevice(this); } /// /// Node has become unanchored from a powernet /// /// /// private void PowernetDisconnect(object sender, PowernetEventArgs eventarg) { eventarg.Powernet.RemoveDevice(this); Connected = DrawTypes.None; ConnectToBestProvider(); } } public enum DrawTypes { None = 0, Node = 1, Provider = 2, Both = 3 } }