Power System (Complete) (#25)

* Power Commit 1

* Commit 2

* Powernet Part2, All components essentially complete

* Commit 4

* Commit 5

* Commit 6

Creates prototypes

* Finishes powernet code alpha

Adds prototypes and logic for powernet updating and regeneration

* Adds onremove functionality to all components

Without these bits of logic nothing makes any sense!

* And this

* Fixes a lot of bugs

* Fix powernet thinking devices are duplicates

* Fix bug and add comments

* woop woop thats the sound of the police
This commit is contained in:
clusterfack
2018-02-03 22:35:42 -06:00
committed by Silver
parent 2eb30c9ba9
commit 1f22f8ab6a
17 changed files with 1642 additions and 32 deletions

View File

@@ -0,0 +1,294 @@
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
{
/// <summary>
/// Component that requires power to function
/// </summary>
public class PowerDeviceComponent : Component
{
public override string Name => "PowerDevice";
/// <summary>
/// The method of draw we will try to use to place our load set via component parameter, defaults to using power providers
/// </summary>
public virtual DrawTypes Drawtype { get; protected set; } = DrawTypes.Provider;
/// <summary>
/// The power draw method we are currently connected to and using
/// </summary>
public DrawTypes Connected { get; protected set; } = DrawTypes.None;
public bool _powered = false;
/// <summary>
/// Status indicator variable for powered
/// </summary>
public virtual bool Powered
{
get => _powered;
set => SetPowered(value);
}
/// <summary>
/// Priority for powernet draw, lower will draw first, defined in powernet.cs
/// </summary>
public virtual Powernet.Priority Priority { get; protected set; } = Powernet.Priority.Medium;
private float _load = 100; //arbitrary magic number to start
/// <summary>
/// Power load from this entity
/// </summary>
public float Load
{
get => _load;
set { UpdateLoad(value); }
}
/// <summary>
/// All the power providers that we are within range of
/// </summary>
public List<PowerProviderComponent> AvailableProviders = new List<PowerProviderComponent>();
private PowerProviderComponent _provider;
/// <summary>
/// A power provider that will handle our load, if we are linked to any
/// </summary>
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<IComponentFactory>();
node = factory.GetComponent<PowerNodeComponent>();
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<DrawTypes>();
}
if (mapping.TryGetNode("Load", out node))
{
Load = node.AsFloat();
}
if (mapping.TryGetNode("Priority", out node))
{
Priority = node.AsEnum<Powernet.Priority>();
}
}
private void UpdateLoad(float value)
{
var oldLoad = _load;
_load = value;
if(Connected == DrawTypes.Node)
{
var node = Owner.GetComponent<PowerNodeComponent>();
node.Parent.UpdateDevice(this, oldLoad);
}
else if(Connected == DrawTypes.Provider)
{
Provider.UpdateDevice(this, oldLoad);
}
}
/// <summary>
/// Changes behavior when receiving a command to become powered or depowered
/// </summary>
/// <param name="value"></param>
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;
}
/// <summary>
/// Register a new power provider as a possible connection to this device
/// </summary>
/// <param name="provider"></param>
public void AddProvider(PowerProviderComponent provider)
{
AvailableProviders.Add(provider);
if(Connected != DrawTypes.Node)
{
ConnectToBestProvider();
}
}
/// <summary>
/// Find the nearest registered power provider and connect to it
/// </summary>
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<TransformComponent>().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<TransformComponent>().WorldPosition - position).LengthSquared;
foreach (var availprovider in AvailableProviders)
{
//Find distance to new provider
var distance = (availprovider.Owner.GetComponent<TransformComponent>().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;
}
/// <summary>
/// Remove a power provider from being a possible connection to this device
/// </summary>
/// <param name="provider"></param>
public void RemoveProvider(PowerProviderComponent provider)
{
if (!AvailableProviders.Contains(provider))
return;
AvailableProviders.Remove(provider);
if (Connected != DrawTypes.Node)
{
ConnectToBestProvider();
}
}
/// <summary>
/// Node has become anchored to a powernet
/// </summary>
/// <param name="sender"></param>
/// <param name="eventarg"></param>
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;
}
/// <summary>
/// Powernet wire was remove so we need to regenerate the powernet
/// </summary>
/// <param name="sender"></param>
/// <param name="eventarg"></param>
private void PowernetRegenerate(object sender, PowernetEventArgs eventarg)
{
eventarg.Powernet.AddDevice(this);
}
/// <summary>
/// Node has become unanchored from a powernet
/// </summary>
/// <param name="sender"></param>
/// <param name="eventarg"></param>
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
}
}

View File

@@ -0,0 +1,105 @@
using SS14.Shared.GameObjects;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Utility;
using System;
using YamlDotNet.RepresentationModel;
namespace Content.Server.GameObjects.Components.Power
{
/// <summary>
/// Component that creates power and supplies it to the powernet
/// </summary>
public class PowerGeneratorComponent : Component
{
public override string Name => "PowerGenerator";
/// <summary>
/// Power supply from this entity
/// </summary>
private float _supply = 1000; //arbitrary initial magic number to start
public float Supply
{
get => _supply;
set { UpdateSupply(value); }
}
public override void LoadParameters(YamlMappingNode mapping)
{
if (mapping.TryGetNode("Supply", out YamlNode node))
{
Supply = node.AsFloat();
}
}
public override void OnAdd(IEntity owner)
{
base.OnAdd(owner);
if (!owner.TryGetComponent(out PowerNodeComponent node))
{
var factory = IoCManager.Resolve<IComponentFactory>();
node = factory.GetComponent<PowerNodeComponent>();
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.RemoveGenerator(this);
}
node.OnPowernetConnect -= PowernetConnect;
node.OnPowernetDisconnect -= PowernetDisconnect;
node.OnPowernetRegenerate -= PowernetRegenerate;
}
base.OnRemove();
}
private void UpdateSupply(float value)
{
_supply = value;
var node = Owner.GetComponent<PowerNodeComponent>();
node.Parent.UpdateGenerator(this);
}
/// <summary>
/// Node has become anchored to a powernet
/// </summary>
/// <param name="sender"></param>
/// <param name="eventarg"></param>
private void PowernetConnect(object sender, PowernetEventArgs eventarg)
{
eventarg.Powernet.AddGenerator(this);
}
/// <summary>
/// Node has had its powernet regenerated
/// </summary>
/// <param name="sender"></param>
/// <param name="eventarg"></param>
private void PowernetRegenerate(object sender, PowernetEventArgs eventarg)
{
eventarg.Powernet.AddGenerator(this);
}
/// <summary>
/// Node has become unanchored from a powernet
/// </summary>
/// <param name="sender"></param>
/// <param name="eventarg"></param>
private void PowernetDisconnect(object sender, PowernetEventArgs eventarg)
{
eventarg.Powernet.RemoveGenerator(this);
}
}
}

View File

@@ -0,0 +1,109 @@
using SS14.Server.GameObjects;
using SS14.Server.Interfaces.GameObjects;
using SS14.Shared.GameObjects;
using SS14.Shared.IoC;
using System;
using System.Linq;
namespace Content.Server.GameObjects.Components.Power
{
/// <summary>
/// Component that connects to the powernet
/// </summary>
public class PowerNodeComponent : Component
{
public override string Name => "PowerNode";
/// <summary>
/// The powernet this node is connected to
/// </summary>
public Powernet Parent;
/// <summary>
/// An event handling when this node connects to a powernet
/// </summary>
public event EventHandler<PowernetEventArgs> OnPowernetConnect;
/// <summary>
/// An event handling when this node disconnects from a powernet
/// </summary>
public event EventHandler<PowernetEventArgs> OnPowernetDisconnect;
/// <summary>
/// An event that registers us to a regenerating powernet
/// </summary>
public event EventHandler<PowernetEventArgs> OnPowernetRegenerate;
public override void Initialize()
{
TryCreatePowernetConnection();
}
public override void OnRemove()
{
DisconnectFromPowernet();
base.OnRemove();
}
/// <summary>
/// Find a nearby wire which will have a powernet and connect ourselves to its powernet
/// </summary>
public void TryCreatePowernetConnection()
{
var _emanager = IoCManager.Resolve<IServerEntityManager>();
var position = Owner.GetComponent<TransformComponent>().WorldPosition;
var wires = _emanager.GetEntitiesIntersecting(Owner)
.Where(x => x.HasComponent<PowerTransferComponent>())
.OrderByDescending(x => (x.GetComponent<TransformComponent>().WorldPosition - position).Length);
var choose = wires.FirstOrDefault();
if(choose != null)
ConnectToPowernet(choose.GetComponent<PowerTransferComponent>().Parent);
}
/// <summary>
/// Triggers event telling power components that we connected to a powernet
/// </summary>
/// <param name="toconnect"></param>
public void ConnectToPowernet(Powernet toconnect)
{
Parent = toconnect;
Parent.Nodelist.Add(this);
OnPowernetConnect?.Invoke(this, new PowernetEventArgs(Parent));
}
/// <summary>
/// Triggers event telling power components that we haven't disconnected but have readded ourselves to a regenerated powernet
/// </summary>
/// <param name="toconnect"></param>
public void RegeneratePowernet(Powernet toconnect)
{
//This removes the device from things that will be powernet disconnected when dirty powernet is killed
Parent.Nodelist.Remove(this);
Parent = toconnect;
Parent.Nodelist.Add(this);
OnPowernetRegenerate?.Invoke(this, new PowernetEventArgs(Parent));
}
/// <summary>
/// Triggers event telling power components we have exited any powernets
/// </summary>
public void DisconnectFromPowernet()
{
Parent.Nodelist.Remove(this);
OnPowernetDisconnect?.Invoke(this, new PowernetEventArgs(Parent));
Parent = null;
}
}
public class PowernetEventArgs : EventArgs
{
public PowernetEventArgs(Powernet powernet)
{
Powernet = powernet;
}
public Powernet Powernet { get; }
}
}

View File

@@ -0,0 +1,201 @@
using SS14.Server.GameObjects;
using SS14.Server.Interfaces.GameObjects;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using YamlDotNet.RepresentationModel;
namespace Content.Server.GameObjects.Components.Power
{
/// <summary>
/// Component that wirelessly connects and powers devices, connects to powernet via node and can be combined with internal storage component
/// </summary>
public class PowerProviderComponent : PowerDeviceComponent
{
public override string Name => "PowerProvider";
/// <inheritdoc />
public override DrawTypes Drawtype { get; protected set; } = DrawTypes.Node;
/// <summary>
/// Variable that determines the range that the power provider will try to supply power to
/// </summary>
public int PowerRange { get; private set; } = 0;
/// <summary>
/// List storing all the power devices that we are currently providing power to
/// </summary>
public SortedSet<PowerDeviceComponent> Deviceloadlist = new SortedSet<PowerDeviceComponent>(new Powernet.DevicePriorityCompare());
public List<PowerDeviceComponent> DepoweredDevices = new List<PowerDeviceComponent>();
public override Powernet.Priority Priority { get; protected set; } = Powernet.Priority.Provider;
public override void LoadParameters(YamlMappingNode mapping)
{
if (mapping.TryGetNode("Range", out YamlNode node))
{
PowerRange = node.AsInt();
}
if (mapping.TryGetNode("Priority", out node))
{
Priority = node.AsEnum<Powernet.Priority>();
}
}
/// <inheritdoc />
public override void SetPowered(bool value)
{
//Let them set us true, we must now power all the devices that rely on us for energy
if (value == true)
{
PowerAllDevices();
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))
{
//Can the storage cover powering all our devices and us? If so power all
if (storage.CanDeductCharge(Load))
{
storage.DeductCharge(Load);
_powered = true;
return;
}
//Does the storage even have any power to give us? If so power as much as we can
else if (storage.RequestAllCharge() != 0)
{
var depowervalue = storage.RequestAllCharge() - Load;
_powered = true;
//See code in powernet for same functionality
foreach (var device in Deviceloadlist)
{
device.Powered = false;
DepoweredDevices.Add(device);
depowervalue -= device.Load;
if (depowervalue < 0)
break;
}
return;
}
//Storage doesn't have anything, depower everything
else if(storage.RequestAllCharge() == 0)
{
DepowerAllDevices();
return;
}
}
//For some reason above we could not power ourselves, we depower ourselves and all devices
DepowerAllDevices();
return;
}
private void PowerAllDevices()
{
_powered = true;
foreach (var device in DepoweredDevices)
{
device.Powered = true;
}
DepoweredDevices.Clear();
}
private void DepowerAllDevices()
{
_powered = false;
foreach (var device in DepoweredDevices)
{
device.Powered = false;
}
}
private void PowernetConnect(object sender, PowernetEventArgs eventarg)
{
eventarg.Powernet.AddDevice(this);
Connected = DrawTypes.Node;
//Find devices within range to take under our control
var _emanager = IoCManager.Resolve<IServerEntityManager>();
var position = Owner.GetComponent<TransformComponent>().WorldPosition;
var entities = _emanager.GetEntitiesInRange(Owner, PowerRange)
.Where(x => x.HasComponent<PowerDeviceComponent>());
foreach (var entity in entities)
{
var device = entity.GetComponent<PowerDeviceComponent>();
//Make sure the device can accept power providers to give it power
if (device.Drawtype == DrawTypes.Provider || device.Drawtype == DrawTypes.Both)
{
device.AddProvider(this);
}
}
}
private void PowernetRegenerate(object sender, PowernetEventArgs eventarg)
{
eventarg.Powernet.AddDevice(this);
}
private void PowernetDisconnect(object sender, PowernetEventArgs eventarg)
{
eventarg.Powernet.RemoveDevice(this);
Connected = DrawTypes.None;
//We don't want to make the devices under us think we're still a valid provider if we have no powernet to connect to
foreach (var device in Deviceloadlist)
{
device.RemoveProvider(this);
}
}
/// <summary>
/// Register a continuous load from a device connected to the powernet
/// </summary>
public void AddDevice(PowerDeviceComponent device)
{
Deviceloadlist.Add(device);
Load += device.Load;
if (!device.Powered)
DepoweredDevices.Add(device);
}
/// <summary>
/// Update one of the loads from a deviceconnected to the powernet
/// </summary>
public void UpdateDevice(PowerDeviceComponent device, float oldLoad)
{
if (Deviceloadlist.Contains(device))
{
Load -= oldLoad;
Load += device.Load;
}
}
/// <summary>
/// Remove a continuous load from a device connected to the powernet
/// </summary>
public void RemoveDevice(PowerDeviceComponent device)
{
if (Deviceloadlist.Contains(device))
{
Load -= device.Load;
Deviceloadlist.Remove(device);
if (DepoweredDevices.Contains(device))
DepoweredDevices.Remove(device);
}
else
{
var name = device.Owner.Prototype.Name;
Logger.Log(String.Format("We tried to remove a device twice from the same {0} somehow, prototype {1}", Name, name));
}
}
}
}

View File

@@ -0,0 +1,201 @@
using SS14.Shared.GameObjects;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.IoC;
using SS14.Shared.Utility;
using System;
using YamlDotNet.RepresentationModel;
namespace Content.Server.GameObjects.Components.Power
{
/// <summary>
/// Feeds energy from the powernet and may have the ability to supply back into it
/// </summary>
public class PowerStorageComponent : Component
{
public override string Name => "PowerStorage";
/// <summary>
/// Maximum amount of energy the internal battery can store
/// </summary>
public float Capacity { get; private set; } = 10000; //arbitrary value replace
/// <summary>
/// Energy the battery is currently storing
/// </summary>
public float Charge { get; private set; } = 0;
/// <summary>
/// Rate at which energy will be taken to charge internal battery
/// </summary>
public float ChargeRate { get; private set; } = 1000;
/// <summary>
/// Rate at which energy will be distributed to the powernet if needed
/// </summary>
public float DistributionRate { get; private set; } = 1000;
private bool _chargepowernet = false;
/// <summary>
/// Do we distribute power into the powernet from our stores if the powernet requires it?
/// </summary>
public bool ChargePowernet
{
get => _chargepowernet;
set
{
_chargepowernet = value;
if (Owner.TryGetComponent(out PowerNodeComponent node))
{
if (node.Parent != null)
node.Parent.UpdateStorageType(this);
}
}
}
public override void LoadParameters(YamlMappingNode mapping)
{
if (mapping.TryGetNode("Capacity", out YamlNode node))
{
Capacity = node.AsFloat();
}
if (mapping.TryGetNode("Charge", out node))
{
Charge = node.AsFloat();
}
if (mapping.TryGetNode("ChargeRate", out node))
{
ChargeRate = node.AsFloat();
}
if (mapping.TryGetNode("DistributionRate", out node))
{
DistributionRate = node.AsFloat();
}
if (mapping.TryGetNode("ChargePowernet", out node))
{
_chargepowernet = node.AsBool();
}
}
public override void OnAdd(IEntity owner)
{
base.OnAdd(owner);
if (!owner.TryGetComponent(out PowerNodeComponent node))
{
var factory = IoCManager.Resolve<IComponentFactory>();
node = factory.GetComponent<PowerNodeComponent>();
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.RemovePowerStorage(this);
}
node.OnPowernetConnect -= PowernetConnect;
node.OnPowernetDisconnect -= PowernetDisconnect;
node.OnPowernetRegenerate -= PowernetRegenerate;
}
base.OnRemove();
}
/// <summary>
/// Checks if the storage can supply the amount of charge directly requested
/// </summary>
public bool CanDeductCharge(float todeduct)
{
if (Charge > todeduct)
return true;
return false;
}
/// <summary>
/// Deducts the requested charge from the energy storage
/// </summary>
public void DeductCharge(float todeduct)
{
Charge = Math.Min(0, Charge - todeduct);
}
/// <summary>
/// Returns all possible charge available from the energy storage
/// </summary>
public float RequestAllCharge()
{
return Math.Min(ChargeRate, Capacity - Charge);
}
/// <summary>
/// Returns the charge available from the energy storage
/// </summary>
public float RequestCharge()
{
return Math.Min(ChargeRate, Capacity - Charge);
}
/// <summary>
/// Returns the charge available from the energy storage
/// </summary>
public float AvailableCharge()
{
return Math.Min(DistributionRate, Charge);
}
/// <summary>
/// Gives the storage one full tick of charging its energy storage
/// </summary>
public void ChargePowerTick()
{
Charge = Math.Max(Charge + ChargeRate, Capacity);
}
/// <summary>
/// Takes from the storage one full tick of energy
/// </summary>
public void RetrievePassiveStorage()
{
Charge = Math.Min(Charge - DistributionRate, 0);
}
/// <summary>
/// Node has become anchored to a powernet
/// </summary>
/// <param name="sender"></param>
/// <param name="eventarg"></param>
private void PowernetConnect(object sender, PowernetEventArgs eventarg)
{
eventarg.Powernet.AddPowerStorage(this);
}
/// <summary>
/// Node has had its powernet regenerated
/// </summary>
/// <param name="sender"></param>
/// <param name="eventarg"></param>
private void PowernetRegenerate(object sender, PowernetEventArgs eventarg)
{
eventarg.Powernet.AddPowerStorage(this);
}
/// <summary>
/// Node has become unanchored from a powernet
/// </summary>
/// <param name="sender"></param>
/// <param name="eventarg"></param>
private void PowernetDisconnect(object sender, PowernetEventArgs eventarg)
{
eventarg.Powernet.RemovePowerStorage(this);
}
}
}

View File

@@ -0,0 +1,129 @@
using SS14.Server.GameObjects;
using SS14.Server.Interfaces.GameObjects;
using SS14.Shared.GameObjects;
using SS14.Shared.IoC;
using System.Linq;
namespace Content.Server.GameObjects.Components.Power
{
/// <summary>
/// Component to transfer power to nearby components, can create powernets and connect to nodes
/// </summary>
public class PowerTransferComponent : Component
{
public override string Name => "PowerTransfer";
/// <summary>
/// The powernet this component is connected to
/// </summary>
public Powernet Parent;
public bool Regenerating { get; set; } = false;
public override void Initialize()
{
if(Parent == null)
{
SpreadPowernet();
}
}
public override void OnRemove()
{
DisconnectFromPowernet();
base.OnRemove();
}
/// <summary>
/// Searches for local powernets to connect to, otherwise creates its own, and spreads powernet to nearby entities
/// </summary>
public void SpreadPowernet()
{
var _emanager = IoCManager.Resolve<IServerEntityManager>();
var position = Owner.GetComponent<TransformComponent>().WorldPosition;
var wires = _emanager.GetEntitiesInRange(Owner, 0.1f) //arbitrarily low, just scrape things //wip
.Where(x => x.HasComponent<PowerTransferComponent>());
//we have no parent so lets find a partner we can join his powernet
if(Parent == null || Regenerating)
{
foreach (var wire in wires)
{
var ptc = wire.GetComponent<PowerTransferComponent>();
if (ptc.CanConnectTo())
{
ConnectToPowernet(ptc.Parent);
break;
}
}
//we couldn't find a partner so none must have spread yet, lets make our own powernet to spread
if (Parent == null || Regenerating)
{
var powernew = new Powernet();
ConnectToPowernet(powernew);
}
}
//Find nodes intersecting us and if not already assigned to a powernet assign them to us
var nodes = _emanager.GetEntitiesIntersecting(Owner)
.Where(x => x.HasComponent<PowerNodeComponent>())
.Select(x => x.GetComponent<PowerNodeComponent>());
foreach(var node in nodes)
{
if(node.Parent == null)
{
node.ConnectToPowernet(Parent);
}
else if(node.Parent.Dirty)
{
node.RegeneratePowernet(Parent);
}
}
//spread powernet to nearby wires which haven't got one yet, and tell them to spread as well
foreach (var wire in wires)
{
var ptc = wire.GetComponent<PowerTransferComponent>();
if (ptc.Parent == null || Regenerating)
{
ptc.ConnectToPowernet(Parent);
SpreadPowernet();
}
else if(ptc.Parent != Parent && !ptc.Parent.Dirty)
{
Parent.MergePowernets(ptc.Parent);
}
}
}
/// <summary>
/// Called when connecting to a new powernet, either on creation or on regeneration
/// </summary>
/// <param name="toconnect"></param>
public void ConnectToPowernet(Powernet toconnect)
{
Parent = toconnect;
Parent.Wirelist.Add(this);
Regenerating = false;
}
/// <summary>
/// Called when we are removed and telling the powernet that it is now dirty and must regenerate
/// </summary>
public void DisconnectFromPowernet()
{
Parent.Wirelist.Remove(this);
Parent.Dirty = true;
Parent = null;
}
public bool CanConnectTo()
{
return Parent != null && Parent.Dirty == false && !Regenerating;
}
}
}

View File

@@ -0,0 +1,402 @@
using Content.Shared.GameObjects.EntitySystems;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using System;
using System.Collections.Generic;
namespace Content.Server.GameObjects.Components.Power
{
/// <summary>
/// Master class for group of powertransfercomponents, takes in and distributes power via nodes
/// </summary>
public class Powernet
{
public Powernet()
{
var EntitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
EntitySystemManager.GetEntitySystem<PowerSystem>().Powernets.Add(this);
}
/// <summary>
/// The entities that make up the powernet's physical location and allow powernet connection
/// </summary>
public List<PowerTransferComponent> Wirelist { get; set; } = new List<PowerTransferComponent>();
/// <summary>
/// Entities that connect directly to the powernet through PTC above to add power or add power load
/// </summary>
public List<PowerNodeComponent> Nodelist { get; set; } = new List<PowerNodeComponent>();
/// <summary>
/// Subset of nodelist that adds a continuous power supply to the network
/// </summary>
public Dictionary<PowerGeneratorComponent, float> Generatorlist { get; set; } = new Dictionary<PowerGeneratorComponent, float>();
/// <summary>
/// Subset of nodelist that draw power, stores information on current continuous powernet load
/// </summary>
public SortedSet<PowerDeviceComponent> Deviceloadlist { get; set; } = new SortedSet<PowerDeviceComponent>(new DevicePriorityCompare());
/// <summary>
/// Comparer that keeps the device dictionary sorted by powernet priority
/// </summary>
public class DevicePriorityCompare : IComparer<PowerDeviceComponent>
{
public int Compare(PowerDeviceComponent x, PowerDeviceComponent y)
{
int compare = y.Priority.CompareTo(x.Priority);
//If the comparer returns 0 sortedset will believe it is a duplicate and return 0, so return 1 instead
if (compare == 0 && !x.Equals(y))
{
return 1;
}
return compare;
}
}
/// <summary>
/// Priority that a device will receive power if powernet cannot supply every device
/// </summary>
public enum Priority
{
Necessary,
High,
Medium,
Low,
Provider,
Unnecessary
}
/// <summary>
/// All the devices that have been depowered by this powernet or depowered prior to being absorted into this powernet
/// </summary>
public List<PowerDeviceComponent> DepoweredDevices { get; set; } = new List<PowerDeviceComponent>();
/// <summary>
/// A list of the energy storage components that will feed the powernet if necessary, and if there is enough power feed itself
/// </summary>
public List<PowerStorageComponent> PowerStorageSupplierlist { get; set; } = new List<PowerStorageComponent>();
/// <summary>
/// A list of energy storage components that will never feed the powernet, will try to draw energy to feed themselves if possible
/// </summary>
public List<PowerStorageComponent> PowerStorageConsumerlist { get; set; } = new List<PowerStorageComponent>();
/// <summary>
/// Static counter of all continuous load placed from devices on this power network
/// </summary>
public float Load { get; private set; } = 0;
/// <summary>
/// Static counter of all continiuous supply from generators on this power network
/// </summary>
public float Supply { get; private set; } = 0;
/// <summary>
/// Variable that causes powernet to be regenerated from its wires during the next update cycle
/// </summary>
public bool Dirty { get; set; } = false;
public void Update(float frametime)
{
float activesupply = Supply;
float activeload = Load;
float storagedemand = 0;
foreach (var supply in PowerStorageConsumerlist)
{
storagedemand += supply.RequestCharge();
}
float passivesupply = 0;
float passivedemand = 0;
foreach (var supply in PowerStorageSupplierlist)
{
passivesupply += supply.AvailableCharge();
passivedemand += supply.RequestCharge();
}
//If we have enough power to feed all load and storage demand, then feed everything
if (activesupply > activeload + storagedemand + passivedemand)
{
PowerAllDevices();
ChargeActiveStorage();
ChargePassiveStorage();
}
//We don't have enough power for the storage powernet suppliers, ignore powering them
else if (activesupply > activeload + storagedemand)
{
PowerAllDevices();
ChargeActiveStorage();
}
//We require the storage powernet suppliers to power the remaining storage components and device load
else if (activesupply + passivesupply > activeload + storagedemand)
{
PowerAllDevices();
ChargeActiveStorage();
RetrievePassiveStorage();
}
//We cant afford to fund the storage components, so lets try to power the basic load using our supply and storage supply
else if (activesupply + passivesupply > activeload)
{
PowerAllDevices();
RetrievePassiveStorage();
}
//We cant even cover the basic device load, start disabling devices in order of priority until the remaining load is lowered enough to be met
else if (activesupply + passivesupply < activeload)
{
PowerAllDevices(); //This merely makes our inevitable betrayal all the sweeter
RetrievePassiveStorage();
var depowervalue = activeload - (activesupply + passivesupply);
//Providers use same method to recreate functionality
foreach(var device in Deviceloadlist)
{
device.Powered = false;
DepoweredDevices.Add(device);
depowervalue -= device.Load;
if (depowervalue < 0)
break;
}
}
}
private void PowerAllDevices()
{
foreach(var device in DepoweredDevices)
{
device.Powered = true;
}
DepoweredDevices.Clear();
}
private void ChargeActiveStorage()
{
foreach (var storage in PowerStorageConsumerlist)
{
storage.ChargePowerTick();
}
}
private void ChargePassiveStorage()
{
foreach (var storage in PowerStorageSupplierlist)
{
storage.ChargePowerTick();
}
}
private void RetrievePassiveStorage()
{
foreach (var storage in PowerStorageSupplierlist)
{
storage.ChargePowerTick();
}
}
/// <summary>
/// Kills a powernet after it is marked dirty and its component have already been regenerated by the powernet system
/// </summary>
public void DirtyKill()
{
Wirelist.Clear();
while(Nodelist.Count != 0)
{
Nodelist[0].DisconnectFromPowernet();
}
Generatorlist.Clear();
Deviceloadlist.Clear();
DepoweredDevices.Clear();
PowerStorageSupplierlist.Clear();
PowerStorageConsumerlist.Clear();
RemoveFromSystem();
}
/// <summary>
/// Combines two powernets when they connect via powertransfer components
/// </summary>
public void MergePowernets(Powernet toMerge)
{
//TODO: load balance reconciliation between powernets on merge tick here
foreach (var wire in toMerge.Wirelist)
{
wire.Parent = this;
}
Wirelist.AddRange(toMerge.Wirelist);
toMerge.Wirelist.Clear();
foreach (var node in toMerge.Nodelist)
{
node.Parent = this;
}
Nodelist.AddRange(toMerge.Nodelist);
toMerge.Nodelist.Clear();
foreach (var generator in toMerge.Generatorlist)
{
Generatorlist.Add(generator.Key, generator.Value);
}
toMerge.Generatorlist.Clear();
foreach (var device in toMerge.Deviceloadlist)
{
Deviceloadlist.Add(device);
}
toMerge.Deviceloadlist.Clear();
DepoweredDevices.AddRange(toMerge.DepoweredDevices);
toMerge.DepoweredDevices.Clear();
PowerStorageSupplierlist.AddRange(toMerge.PowerStorageSupplierlist);
toMerge.PowerStorageSupplierlist.Clear();
PowerStorageConsumerlist.AddRange(toMerge.PowerStorageConsumerlist);
toMerge.PowerStorageConsumerlist.Clear();
toMerge.RemoveFromSystem();
}
/// <summary>
/// Removes reference from the powernets list on the powernet system
/// </summary>
private void RemoveFromSystem()
{
var EntitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
EntitySystemManager.GetEntitySystem<PowerSystem>().Powernets.Remove(this);
}
#region Registration
/// <summary>
/// Register a continuous load from a device connected to the powernet
/// </summary>
public void AddDevice(PowerDeviceComponent device)
{
Deviceloadlist.Add(device);
Load += device.Load;
if (!device.Powered)
DepoweredDevices.Add(device);
}
/// <summary>
/// Update one of the loads from a deviceconnected to the powernet
/// </summary>
public void UpdateDevice(PowerDeviceComponent device, float oldLoad)
{
if(Deviceloadlist.Contains(device))
{
Load -= oldLoad;
Load += device.Load;
}
}
/// <summary>
/// Remove a continuous load from a device connected to the powernet
/// </summary>
public void RemoveDevice(PowerDeviceComponent device)
{
if(Deviceloadlist.Contains(device))
{
Load -= device.Load;
Deviceloadlist.Remove(device);
if (DepoweredDevices.Contains(device))
DepoweredDevices.Remove(device);
}
else
{
var name = device.Owner.Prototype.Name;
Logger.Log(String.Format("We tried to remove a device twice from the same powernet somehow, prototype {0}", name));
}
}
/// <summary>
/// Register a power supply from a generator connected to the powernet
/// </summary>
public void AddGenerator(PowerGeneratorComponent generator)
{
Generatorlist.Add(generator, generator.Supply);
Supply += generator.Supply;
}
/// <summary>
/// Update the value supplied from a generator connected to the powernet
/// </summary>
public void UpdateGenerator(PowerGeneratorComponent generator)
{
if (Generatorlist.ContainsKey(generator))
{
Supply -= Generatorlist[generator];
Generatorlist[generator] = generator.Supply;
Supply += generator.Supply;
}
}
/// <summary>
/// Remove a power supply from a generator connected to the powernet
/// </summary>
public void RemoveGenerator(PowerGeneratorComponent generator)
{
if (Generatorlist.ContainsKey(generator))
{
Supply -= Generatorlist[generator];
Generatorlist.Remove(generator);
}
else
{
var name = generator.Owner.Prototype.Name;
Logger.Log(String.Format("We tried to remove a device twice from the same power somehow, prototype {1}", name));
}
}
/// <summary>
/// Register a power supply from a generator connected to the powernet
/// </summary>
public void AddPowerStorage(PowerStorageComponent storage)
{
if(storage.ChargePowernet)
PowerStorageSupplierlist.Add(storage);
else
PowerStorageConsumerlist.Add(storage);
}
//How do I even call this? TODO: fix
public void UpdateStorageType(PowerStorageComponent storage)
{
//If our chargepowernet settings change we need to tell the powernet of this new setting and remove traces of our old setting
if (PowerStorageSupplierlist.Contains(storage))
PowerStorageSupplierlist.Remove(storage);
if (PowerStorageConsumerlist.Contains(storage))
PowerStorageConsumerlist.Remove(storage);
//Apply new setting
if (storage.ChargePowernet)
PowerStorageSupplierlist.Add(storage);
else
PowerStorageConsumerlist.Add(storage);
}
/// <summary>
/// Remove a power supply from a generator connected to the powernet
/// </summary>
public void RemovePowerStorage(PowerStorageComponent storage)
{
if (PowerStorageSupplierlist.Contains(storage))
{
PowerStorageSupplierlist.Remove(storage);
}
if (PowerStorageConsumerlist.Contains(storage))
{
PowerStorageSupplierlist.Remove(storage);
}
}
#endregion Registration
}
}