mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-30 08:07:28 +00:00
* Fix the sensor monitoring console forcing a GC every 3 seconds (#38146) * Optimize sensor monitoring window graph drawing * Add shared static Vector2 pool for all GraphView instances * Address requested changes * remove lock * Update submodule to 264.0.0 (#38629) * Toy/Plushie Inhands and Wearables (#38514) * Fixed dependency injection and some other issues in a few places. * More issue fixes * patchfor broken NetworkConfiguratorLinkMenu (#38632) fix * Fix solutions flickering when transferring contents (#34838) * Use Solution clones when applying SolutionComponent states * Revert "Use Solution clones when applying SolutionComponent states" This reverts commit 013fd111cf92b22562e00f98a7aaa49bc4b4ed62. * Make Solution implement ICloneable and rename Clone method. * Copy CanReact value when cloning a Solution * Convert to IRobustCloneable * Fix typos in guidebook: Buisness → Business (#38636) * Fix typo in `MinorAntagonists.xml` Buisness → Business * Fix typo in `YourFirstCharacter.xml` Buisness → Business * Various Headphones Fixes and Tweaks (#38479) * sprites, wearables * neck * icon-on sprite * Cleanup prototype instantiation in `DamageTest` (#38639) Cleanup prototype instantiation in DamageTest * Cleanup prototype instantiation in `ExplosionSystem` (#38642) Cleanup prototype instantiation in ExplosionSystem * AddBodyPartCommand localization. (#38612) commit * fix ItemSlotsSystem debug assert (#38655) * Allow the Command & Super door remotes to use the access of their user. (Re-creation of PR due to changes to game balance) (#35536) * Added directional beacons (#38284) * Added directional beacons Signed-off-by: Nox38 <nebulousnox38@gmail.com> * Fixed names Signed-off-by: Nox38 <nebulousnox38@gmail.com> --------- Signed-off-by: Nox38 <nebulousnox38@gmail.com> * Power stat and nuke codes commands get some LEC love. (#38585) * commit * requested changes. * Dsay Dirty and Follow commands converted to LEC and localized. (#38666) * commit * whoopwhoopwhoop * Retro laser sprite fix (#38676) * Fixed everything except the icon Signed-off-by: Nox38 <nebulousnox38@gmail.com> * fixed icon Signed-off-by: Nox38 <nebulousnox38@gmail.com> --------- Signed-off-by: Nox38 <nebulousnox38@gmail.com> * fix water coolers (#38681) * Monochromacy typo fix (#38686) * fixes the typo * Fixed cloning looking for the trait, not the component, RE https://github.com/space-wizards/space-station-14/pull/38686#issuecomment-3025093504 * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Validate `CloningSettingsPrototype`s (#38688) * Validate CloningSettingsPrototypes * Update Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Check EventComponents too --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Add test of objective-related console commands (#36400) * Add test of objective add/list/remove commands * Not sure why we're validating test prototypes, but sure * We don't need a map * Improvements and fixups for New Status Effect API (#38660) * Predict healing and bloodstream (#38690) * initial commit * reapply 38126 * fix rootable * someone missed an important minus sign here * try this * fix * fix * reenable crit hits * cleanup * fix status time dirtying * fix * camelCase * Healing and bloodstream prediction fixes. * Resolving Wizard casting recall on nuke disk making it impossible to disarm (#38661) * Resolving Wizard Recall on Nuke disk making it impossible to disarm - Adding a DisarmBomb case to nuke status update loop - Changing a few methods and parameters to properly follow formatting standards - Updating some names to follow camelCase * Updating missed tag * Reverting DataField change Should prevent this preventative bugfix being a breaking change. * Switch HSV to the default colorspace for character customization (#38434) * Made HSV default for character editor * Adds/fixes comments to HSV defaulting * Added dropbox fix, potentially cursed * Revert "Added dropbox fix, potentially cursed" This reverts commit a709883366fbee813e839742125e70844672af29. --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * Fix: Don't deploy foldables when clicking on items inside containers (#38709) * Fix * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Pressure Relief Valve (#36708) * initial system (this math is probably WRONG) * General code cleanup and OnExamined support (holy moly this code sucks) * UICode and related events foundation TODO: - Actually write the XAML UI and the underlying system - Un-shitcode the entire thing - Actually test everything... * Working UI code TODO: Make predicted, as this certainly isn't predicted. Even though I said it was. It isn't. * Remove one TODO for unshitcoding the examine code * Add reminder yea * Make predicted (defenitely isn't) (also defenitely isn't a copypaste from pressure pump code) * It's predicted! TODO: - Give it snazzy predicted visuals! - Have a different field for pressure entry, lest it gets bulldozed every UI update. * Improve gas pressure relief valve UI TODO: Reminder to reduce amount of dirties using deltafields * Implement DirtyField prediction * Entity<T> cleanup A lot of Entity<T> conversions and lukewarm cleanup. Also got caught copy pasting code in 4K UHD but it's not like you couldn't tell. * More cleanup and comments * Remove TODO comment on bulldozing window title * """refactoring""" - Move appearance out of shared and finally fix it. Pointless to predict appearance in this instance. - More Entity<T> conversions because I like them. - Move UI creation handling over entirely to the ActivatableUI system. - Fix a hardcoded locale string (why????). * Add visuals * Revert debugging variable replacememt yea * Revert skissue * Remove unused using directives and remove TODO * Localize, cleanup, document * Fix adminlogging discrepancy * Add ability to construct, add guidebook entry * Clear up comment * Add guidebook tooltip to valve * Convert GasPressureReliefValveBoundUserInterface declaration into primary constructor * Adds more input handling and adds autofill on open * Un-deepfry input validator shitcode Genuinely what was I smoking * improve visuals logic * Refactor again - Update math to the correct implementation - Moved code that could be re-used in the future into a helper method under AtmosphereSystem.Gases.cs * I'm sorry but I hate warnings * Remove unused using directive in AtmosphereSystem.Gases.cs * Review and cleanup * Lukewarm UI glossup * Maintainer for the upstream project btw * Remove redundant state sets and messy logic * Unduplicate valve updater code * Redo UI (im sorry Slarti) * run tests * Test refactored UI messaging * Second round of UI improvements - God please find a way to improve this system. Feels bad. * Update loop implementation * Further predict UI * Clear up SetToCurrentThreshold * cleanup * Update to master + pipe layers and bug fixes want to run tests * fixes * Deploy rename pipebomb * Documentation and requested changes * Rename the method that wiggled away * Undo rounding changes * Fix comment * Rename and cleanup * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Fix TextLinkTag (#32203) * Adjust uplink buy button to be under item icon (#38596) * Adjusted uplink buy button to be under item icon * Put the discount subtext under the icon * Indent fixes, added margin --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * Scurrets - Audio Improvements (#38482) * Scurret audio tuning * Add new sfx * Update sneezing sfx * YAML support * Rename a folder * make telesci wreck easier (#37569) rel * Validate `ShaderPrototype` IDs (#38728) * Convert all shader prototype string literals to protoids in overlays * Convert more shader prototype literal strings to protoids * Convert ValidatePrototypeId to ProtoId * Later * [BUGFIX] Fixed revenant malfunction ability not working properly only MediBots and Stasis bed (#38664) * fixed * clean up * orks fix smart * review fix 1 * more requested changes * less cursed * more descriptive description * better wording * Tiny Tiny Cleanup of the EyeClosingSystem. (#38734) Update EyeClosingSystem.cs * Fix human skin tone distribution (#38701) * fix: Use PredictedQueueDel for gib spell (#38729) * fix: don't default to uncharged sprite state for cells (#38730) * Fix Hristov description - remove inaccurate technical specs (#38746) - Removes inaccurate 'armor piercing 14.5mm shells' reference - Replaces it with a more funny description, matching the style of the other snipers and guns - Keeps ammunition type '.60 anti-materiel ammo' specification - Fixes issue #38590 Co-authored-by: Arthur Fiorese de Andrade <aandrade@cmcxs.gov.br> * feat: allow mopping evaporating puddles (#38743) * Validate remaining `ProtoId` strings (#38747) Validate remaining ProtoId strings * Validate `ProtoId`s in tests (#38745) * Convert string literals to protoids in Content.Tests * Convert string literals to protoids or consts in Content.IntegrationTests * Fix linter failures Tricksy static using misled me * Cleanup warning in StomachSystem (#38748) you did not see this * Vox scars (#38592) * Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl * Revert "Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl" This reverts commit c73da55ba3b39ddf93b493aecd85604c54dd8a15. * locale key fix * Changed top surgery scar names to be more generalized * Adjusted face scars * Formatting fixes --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * UnlockNode command to LEC. (#38751) * commit * Update UnlockNodeCommand.cs * commit * move command locale to its own file. * Update Content.Server/Xenoarchaeology/Artifact/UnlockNodeCommand.cs --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Typofixes for figurine dialogue (#38737) * Typofixes for figurine dialogue * Forgot two * Janitor Tool: Wire Brush (#38667) * Wow! It's -brush- * spacing. * Update Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * fixed changes --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Minor escape menu UX improvements (#38650) * fix: spellbooks can have infinite charges (#38376) * fix: spellbooks can have infinite charges * refactor: indicate infinite spellbook charges with null Not sure if I like this much better...
1019 lines
38 KiB
C#
1019 lines
38 KiB
C#
using Content.Server.NodeContainer;
|
|
using Content.Server.NodeContainer.EntitySystems;
|
|
using Content.Server.NodeContainer.Nodes;
|
|
using Content.Server.Power.Components;
|
|
using Content.Server.Power.Nodes;
|
|
using Content.Server.Power.NodeGroups;
|
|
using Content.Server.StationEvents.Components;
|
|
using Content.Shared.GameTicking.Components;
|
|
using Content.Shared.Pinpointer;
|
|
using Content.Shared.Station.Components;
|
|
using Content.Shared.Power;
|
|
using JetBrains.Annotations;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Shared.Map.Components;
|
|
using Robust.Shared.Utility;
|
|
using System.Linq;
|
|
using Content.Shared.NodeContainer;
|
|
|
|
namespace Content.Server.Power.EntitySystems;
|
|
|
|
[UsedImplicitly]
|
|
internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitoringConsoleSystem
|
|
{
|
|
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
|
[Dependency] private readonly SharedMapSystem _sharedMapSystem = default!;
|
|
|
|
// Note: this data does not need to be saved
|
|
private Dictionary<EntityUid, Dictionary<Vector2i, PowerCableChunk>> _gridPowerCableChunks = new();
|
|
private float _updateTimer = 1.0f;
|
|
|
|
private const float UpdateTime = 1.0f;
|
|
private const float RoguePowerConsumerThreshold = 100000;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
// Console events
|
|
SubscribeLocalEvent<PowerMonitoringConsoleComponent, ComponentInit>(OnConsoleInit);
|
|
SubscribeLocalEvent<PowerMonitoringConsoleComponent, EntParentChangedMessage>(OnConsoleParentChanged);
|
|
SubscribeLocalEvent<PowerMonitoringCableNetworksComponent, ComponentInit>(OnCableNetworksInit);
|
|
SubscribeLocalEvent<PowerMonitoringCableNetworksComponent, EntParentChangedMessage>(OnCableNetworksParentChanged);
|
|
|
|
// UI events
|
|
SubscribeLocalEvent<PowerMonitoringConsoleComponent, PowerMonitoringConsoleMessage>(OnPowerMonitoringConsoleMessage);
|
|
SubscribeLocalEvent<PowerMonitoringConsoleComponent, BoundUIOpenedEvent>(OnBoundUIOpened);
|
|
|
|
// Grid events
|
|
SubscribeLocalEvent<GridSplitEvent>(OnGridSplit);
|
|
SubscribeLocalEvent<CableComponent, CableAnchorStateChangedEvent>(OnCableAnchorStateChanged);
|
|
SubscribeLocalEvent<PowerMonitoringDeviceComponent, AnchorStateChangedEvent>(OnDeviceAnchoringChanged);
|
|
SubscribeLocalEvent<PowerMonitoringDeviceComponent, NodeGroupsRebuilt>(OnNodeGroupRebuilt);
|
|
|
|
// Game rule events
|
|
SubscribeLocalEvent<GameRuleStartedEvent>(OnPowerGridCheckStarted);
|
|
SubscribeLocalEvent<GameRuleEndedEvent>(OnPowerGridCheckEnded);
|
|
}
|
|
|
|
#region EventHandling
|
|
|
|
private void OnConsoleInit(EntityUid uid, PowerMonitoringConsoleComponent component, ComponentInit args)
|
|
{
|
|
RefreshPowerMonitoringConsole(uid, component);
|
|
}
|
|
|
|
private void OnConsoleParentChanged(EntityUid uid, PowerMonitoringConsoleComponent component, EntParentChangedMessage args)
|
|
{
|
|
RefreshPowerMonitoringConsole(uid, component);
|
|
}
|
|
|
|
private void OnCableNetworksInit(EntityUid uid, PowerMonitoringCableNetworksComponent component, ComponentInit args)
|
|
{
|
|
RefreshPowerMonitoringCableNetworks(uid, component);
|
|
}
|
|
|
|
private void OnCableNetworksParentChanged(EntityUid uid, PowerMonitoringCableNetworksComponent component, EntParentChangedMessage args)
|
|
{
|
|
RefreshPowerMonitoringCableNetworks(uid, component);
|
|
}
|
|
|
|
private void OnPowerMonitoringConsoleMessage(EntityUid uid, PowerMonitoringConsoleComponent component, PowerMonitoringConsoleMessage args)
|
|
{
|
|
var focus = GetEntity(args.FocusDevice);
|
|
var group = args.FocusGroup;
|
|
|
|
// Update this if the focus device has changed
|
|
if (component.Focus != focus)
|
|
{
|
|
component.Focus = focus;
|
|
|
|
if (TryComp<PowerMonitoringCableNetworksComponent>(uid, out var cableNetworks))
|
|
{
|
|
cableNetworks.FocusChunks.Clear(); // Component will be dirtied when these chunks are rebuilt, unless the focus is null
|
|
|
|
if (focus == null)
|
|
Dirty(uid, cableNetworks);
|
|
}
|
|
}
|
|
|
|
// Update this if the focus group has changed
|
|
if (component.FocusGroup != group)
|
|
{
|
|
component.FocusGroup = args.FocusGroup;
|
|
Dirty(uid, component);
|
|
}
|
|
}
|
|
|
|
private void OnBoundUIOpened(EntityUid uid, PowerMonitoringConsoleComponent component, BoundUIOpenedEvent args)
|
|
{
|
|
component.Focus = null;
|
|
component.FocusGroup = PowerMonitoringConsoleGroup.Generator;
|
|
|
|
if (TryComp<PowerMonitoringCableNetworksComponent>(uid, out var cableNetworks))
|
|
{
|
|
cableNetworks.FocusChunks.Clear();
|
|
Dirty(uid, cableNetworks);
|
|
}
|
|
}
|
|
|
|
private void OnGridSplit(ref GridSplitEvent args)
|
|
{
|
|
// Collect grids
|
|
var allGrids = args.NewGrids.ToList();
|
|
|
|
if (!allGrids.Contains(args.Grid))
|
|
allGrids.Add(args.Grid);
|
|
|
|
// Refresh affected power cable grids
|
|
foreach (var grid in allGrids)
|
|
{
|
|
if (!TryComp<MapGridComponent>(grid, out var map))
|
|
continue;
|
|
|
|
RefreshPowerCableGrid(grid, map);
|
|
}
|
|
|
|
// Update power monitoring consoles that stand upon an updated grid
|
|
var query = AllEntityQuery<PowerMonitoringConsoleComponent, PowerMonitoringCableNetworksComponent, TransformComponent>();
|
|
while (query.MoveNext(out var ent, out var entConsole, out var entCableNetworks, out var entXform))
|
|
{
|
|
if (entXform.GridUid == null)
|
|
continue;
|
|
|
|
if (!allGrids.Contains(entXform.GridUid.Value))
|
|
continue;
|
|
|
|
RefreshPowerMonitoringConsole(ent, entConsole);
|
|
RefreshPowerMonitoringCableNetworks(ent, entCableNetworks);
|
|
}
|
|
}
|
|
|
|
public void OnCableAnchorStateChanged(EntityUid uid, CableComponent component, CableAnchorStateChangedEvent args)
|
|
{
|
|
var xform = args.Transform;
|
|
|
|
if (xform.GridUid == null || !TryComp<MapGridComponent>(xform.GridUid, out var grid))
|
|
return;
|
|
|
|
if (!_gridPowerCableChunks.TryGetValue(xform.GridUid.Value, out var allChunks))
|
|
allChunks = new();
|
|
|
|
var tile = _sharedMapSystem.LocalToTile(xform.GridUid.Value, grid, xform.Coordinates);
|
|
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, ChunkSize);
|
|
|
|
if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
|
|
{
|
|
chunk = new PowerCableChunk(chunkOrigin);
|
|
allChunks[chunkOrigin] = chunk;
|
|
}
|
|
|
|
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
|
|
var flag = GetFlag(relative);
|
|
|
|
if (args.Anchored)
|
|
chunk.PowerCableData[(int) component.CableType] |= flag;
|
|
|
|
else
|
|
chunk.PowerCableData[(int) component.CableType] &= ~flag;
|
|
|
|
var query = AllEntityQuery<PowerMonitoringCableNetworksComponent, TransformComponent>();
|
|
while (query.MoveNext(out var ent, out var entCableNetworks, out var entXform))
|
|
{
|
|
if (entXform.GridUid != xform.GridUid)
|
|
continue;
|
|
|
|
entCableNetworks.AllChunks = allChunks;
|
|
Dirty(ent, entCableNetworks);
|
|
}
|
|
}
|
|
|
|
private void OnDeviceAnchoringChanged(EntityUid uid, PowerMonitoringDeviceComponent component, AnchorStateChangedEvent args)
|
|
{
|
|
var xform = Transform(uid);
|
|
var gridUid = xform.GridUid;
|
|
|
|
if (gridUid == null)
|
|
return;
|
|
|
|
if (component.IsCollectionMasterOrChild)
|
|
AssignEntityAsCollectionMaster(uid, component, xform);
|
|
|
|
var query = AllEntityQuery<PowerMonitoringConsoleComponent, TransformComponent>();
|
|
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
|
|
{
|
|
if (gridUid != entXform.GridUid)
|
|
continue;
|
|
|
|
if (!args.Anchored)
|
|
{
|
|
entConsole.PowerMonitoringDeviceMetaData.Remove(GetNetEntity(uid));
|
|
Dirty(ent, entConsole);
|
|
|
|
continue;
|
|
}
|
|
|
|
var name = MetaData(uid).EntityName;
|
|
var coords = GetNetCoordinates(xform.Coordinates);
|
|
|
|
var metaData = new PowerMonitoringDeviceMetaData(name, coords, component.Group, component.SpritePath, component.SpriteState);
|
|
entConsole.PowerMonitoringDeviceMetaData.TryAdd(GetNetEntity(uid), metaData);
|
|
|
|
Dirty(ent, entConsole);
|
|
}
|
|
}
|
|
|
|
public void OnNodeGroupRebuilt(EntityUid uid, PowerMonitoringDeviceComponent component, NodeGroupsRebuilt args)
|
|
{
|
|
if (component.IsCollectionMasterOrChild)
|
|
AssignEntityAsCollectionMaster(uid, component);
|
|
|
|
var query = AllEntityQuery<PowerMonitoringConsoleComponent, PowerMonitoringCableNetworksComponent>();
|
|
while (query.MoveNext(out var _, out var entConsole, out var entCableNetworks))
|
|
{
|
|
if (entConsole.Focus == uid)
|
|
entCableNetworks.FocusChunks.Clear(); // Component is dirtied when these chunks are rebuilt
|
|
}
|
|
}
|
|
|
|
private void OnPowerGridCheckStarted(ref GameRuleStartedEvent ev)
|
|
{
|
|
if (!TryComp<PowerGridCheckRuleComponent>(ev.RuleEntity, out var rule))
|
|
return;
|
|
|
|
var query = AllEntityQuery<PowerMonitoringConsoleComponent, TransformComponent>();
|
|
while (query.MoveNext(out var uid, out var console, out var xform))
|
|
{
|
|
if (CompOrNull<StationMemberComponent>(xform.GridUid)?.Station == rule.AffectedStation)
|
|
{
|
|
console.Flags |= PowerMonitoringFlags.PowerNetAbnormalities;
|
|
Dirty(uid, console);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnPowerGridCheckEnded(ref GameRuleEndedEvent ev)
|
|
{
|
|
if (!TryComp<PowerGridCheckRuleComponent>(ev.RuleEntity, out var rule))
|
|
return;
|
|
|
|
var query = AllEntityQuery<PowerMonitoringConsoleComponent, TransformComponent>();
|
|
while (query.MoveNext(out var uid, out var console, out var xform))
|
|
{
|
|
if (CompOrNull<StationMemberComponent>(xform.GridUid)?.Station == rule.AffectedStation)
|
|
{
|
|
console.Flags &= ~PowerMonitoringFlags.PowerNetAbnormalities;
|
|
Dirty(uid, console);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
|
|
_updateTimer += frameTime;
|
|
|
|
if (_updateTimer >= UpdateTime)
|
|
{
|
|
_updateTimer -= UpdateTime;
|
|
|
|
var query = AllEntityQuery<PowerMonitoringConsoleComponent>();
|
|
while (query.MoveNext(out var ent, out var console))
|
|
{
|
|
if (!_userInterfaceSystem.IsUiOpen(ent, PowerMonitoringConsoleUiKey.Key))
|
|
continue;
|
|
|
|
UpdateUIState(ent, console);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateUIState(EntityUid uid, PowerMonitoringConsoleComponent component)
|
|
{
|
|
var consoleXform = Transform(uid);
|
|
|
|
if (consoleXform?.GridUid == null)
|
|
return;
|
|
|
|
var gridUid = consoleXform.GridUid.Value;
|
|
|
|
if (!TryComp<MapGridComponent>(gridUid, out var mapGrid))
|
|
return;
|
|
|
|
// The grid must have a NavMapComponent to visualize the map in the UI
|
|
EnsureComp<NavMapComponent>(gridUid);
|
|
|
|
// Initializing data to be send to the client
|
|
var totalSources = 0d;
|
|
var totalBatteryUsage = 0d;
|
|
var totalLoads = 0d;
|
|
var allEntries = new List<PowerMonitoringConsoleEntry>();
|
|
var sourcesForFocus = new List<PowerMonitoringConsoleEntry>();
|
|
var loadsForFocus = new List<PowerMonitoringConsoleEntry>();
|
|
var flags = component.Flags;
|
|
|
|
// Reset RoguePowerConsumer flag
|
|
component.Flags &= ~PowerMonitoringFlags.RoguePowerConsumer;
|
|
|
|
// Record the load value of all non-tracked power consumers on the same grid as the console
|
|
var powerConsumerQuery = AllEntityQuery<PowerConsumerComponent, TransformComponent>();
|
|
while (powerConsumerQuery.MoveNext(out var ent, out var powerConsumer, out var xform))
|
|
{
|
|
if (xform.Anchored == false || xform.GridUid != gridUid)
|
|
continue;
|
|
|
|
if (TryComp<PowerMonitoringDeviceComponent>(ent, out var device))
|
|
continue;
|
|
|
|
// Flag an alert if power consumption is ridiculous
|
|
if (powerConsumer.ReceivedPower >= RoguePowerConsumerThreshold)
|
|
component.Flags |= PowerMonitoringFlags.RoguePowerConsumer;
|
|
|
|
totalLoads += powerConsumer.DrawRate;
|
|
}
|
|
|
|
if (component.Flags != flags)
|
|
Dirty(uid, component);
|
|
|
|
// Loop over all tracked devices
|
|
var powerMonitoringDeviceQuery = AllEntityQuery<PowerMonitoringDeviceComponent, TransformComponent>();
|
|
while (powerMonitoringDeviceQuery.MoveNext(out var ent, out var device, out var xform))
|
|
{
|
|
// Ignore joint, non-master entities
|
|
if (device.IsCollectionMasterOrChild && !device.IsCollectionMaster)
|
|
continue;
|
|
|
|
if (xform.Anchored == false || xform.GridUid != gridUid)
|
|
continue;
|
|
|
|
// Get the device power stats
|
|
var powerStats = GetPowerStats(ent, device);
|
|
//, out var powerSupplied, out var powerUsage, out var batteryUsage);
|
|
|
|
// Update all running totals
|
|
totalSources += powerStats.PowerSupplied;
|
|
totalLoads += powerStats.PowerUsage;
|
|
totalBatteryUsage += powerStats.BatteryUsage;
|
|
|
|
// Continue on if the device is not in the current focus group
|
|
if (device.Group != component.FocusGroup)
|
|
continue;
|
|
|
|
// Generate a new console entry with which to populate the UI
|
|
var entry = new PowerMonitoringConsoleEntry(GetNetEntity(ent), device.Group, powerStats.PowerValue, powerStats.BatteryLevel);
|
|
allEntries.Add(entry);
|
|
}
|
|
|
|
// Update the UI focus data (if applicable)
|
|
if (component.Focus != null)
|
|
{
|
|
if (TryComp<NodeContainerComponent>(component.Focus, out var nodeContainer) &&
|
|
TryComp<PowerMonitoringDeviceComponent>(component.Focus, out var device))
|
|
{
|
|
// Record the tracked sources powering the device
|
|
if (nodeContainer.Nodes.TryGetValue(device.SourceNode, out var sourceNode))
|
|
GetSourcesForNode(component.Focus.Value, sourceNode, out sourcesForFocus);
|
|
|
|
// Search for the enabled load node (required for portable generators)
|
|
var loadNodeName = device.LoadNode;
|
|
|
|
if (device.LoadNodes != null)
|
|
{
|
|
var foundNode = nodeContainer.Nodes.FirstOrNull(x => x.Value is CableDeviceNode && (x.Value as CableDeviceNode)?.Enabled == true);
|
|
|
|
if (foundNode != null)
|
|
loadNodeName = foundNode.Value.Key;
|
|
}
|
|
|
|
// Record the tracked loads on the device
|
|
if (nodeContainer.Nodes.TryGetValue(loadNodeName, out var loadNode))
|
|
GetLoadsForNode(component.Focus.Value, loadNode, out loadsForFocus);
|
|
|
|
// If the UI focus changed, update the highlighted power network
|
|
if (TryComp<PowerMonitoringCableNetworksComponent>(uid, out var cableNetworks) &&
|
|
cableNetworks.FocusChunks.Count == 0)
|
|
{
|
|
var reachableEntities = new List<EntityUid>();
|
|
|
|
if (sourceNode?.NodeGroup != null)
|
|
{
|
|
foreach (var node in sourceNode.NodeGroup.Nodes)
|
|
reachableEntities.Add(node.Owner);
|
|
}
|
|
|
|
if (loadNode?.NodeGroup != null)
|
|
{
|
|
foreach (var node in loadNode.NodeGroup.Nodes)
|
|
reachableEntities.Add(node.Owner);
|
|
}
|
|
|
|
UpdateFocusNetwork(uid, cableNetworks, gridUid, mapGrid, reachableEntities);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the UI state
|
|
_userInterfaceSystem.SetUiState(uid,
|
|
PowerMonitoringConsoleUiKey.Key,
|
|
new PowerMonitoringConsoleBoundInterfaceState
|
|
(totalSources,
|
|
totalBatteryUsage,
|
|
totalLoads,
|
|
allEntries.ToArray(),
|
|
sourcesForFocus.ToArray(),
|
|
loadsForFocus.ToArray()));
|
|
}
|
|
|
|
private PowerStats GetPowerStats(EntityUid uid, PowerMonitoringDeviceComponent device)
|
|
{
|
|
var stats = new PowerStats();
|
|
|
|
if (device.Group == PowerMonitoringConsoleGroup.Generator)
|
|
{
|
|
// This covers most power sources
|
|
if (TryComp<PowerSupplierComponent>(uid, out var supplier))
|
|
{
|
|
stats.PowerValue = supplier.CurrentSupply;
|
|
stats.PowerSupplied += stats.PowerValue;
|
|
}
|
|
|
|
// Edge case: radiation collectors
|
|
else if (TryComp<BatteryDischargerComponent>(uid, out var _) &&
|
|
TryComp<PowerNetworkBatteryComponent>(uid, out var battery))
|
|
{
|
|
stats.PowerValue = battery.NetworkBattery.CurrentSupply;
|
|
stats.PowerSupplied += stats.PowerValue;
|
|
|
|
|
|
stats.BatteryLevel = GetBatteryLevel(uid);
|
|
}
|
|
}
|
|
|
|
else if (device.Group == PowerMonitoringConsoleGroup.SMES ||
|
|
device.Group == PowerMonitoringConsoleGroup.Substation ||
|
|
device.Group == PowerMonitoringConsoleGroup.APC)
|
|
{
|
|
|
|
if (TryComp<PowerNetworkBatteryComponent>(uid, out var battery))
|
|
{
|
|
stats.BatteryLevel = GetBatteryLevel(uid);
|
|
|
|
stats.PowerValue = battery.CurrentSupply;
|
|
|
|
// Load due to network battery recharging
|
|
stats.PowerUsage += Math.Max(battery.CurrentReceiving - battery.CurrentSupply, 0d);
|
|
|
|
// Track battery usage
|
|
stats.BatteryUsage += Math.Max(battery.CurrentSupply - battery.CurrentReceiving, 0d);
|
|
|
|
// Records loads attached to APCs
|
|
if (device.Group == PowerMonitoringConsoleGroup.APC && battery.Enabled)
|
|
{
|
|
stats.PowerUsage += battery.NetworkBattery.LoadingNetworkDemand;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Master devices add the power values from all entities they represent (if applicable)
|
|
if (device.IsCollectionMasterOrChild && device.IsCollectionMaster)
|
|
{
|
|
foreach ((var child, var childDevice) in device.ChildDevices)
|
|
{
|
|
if (child == uid)
|
|
continue;
|
|
|
|
// Safeguard to prevent infinite loops
|
|
if (childDevice.IsCollectionMaster && childDevice.ChildDevices.ContainsKey(uid))
|
|
continue;
|
|
|
|
var childResult = GetPowerStats(child, childDevice);
|
|
|
|
stats.PowerValue += childResult.PowerValue;
|
|
stats.PowerSupplied += childResult.PowerSupplied;
|
|
stats.PowerUsage += childResult.PowerUsage;
|
|
stats.BatteryUsage += childResult.BatteryUsage;
|
|
}
|
|
}
|
|
|
|
return stats;
|
|
}
|
|
|
|
private float? GetBatteryLevel(EntityUid uid)
|
|
{
|
|
if (!TryComp<BatteryComponent>(uid, out var battery))
|
|
return null;
|
|
|
|
var effectiveMax = battery.MaxCharge;
|
|
if (effectiveMax == 0)
|
|
effectiveMax = 1;
|
|
|
|
return battery.CurrentCharge / effectiveMax;
|
|
}
|
|
|
|
private void GetSourcesForNode(EntityUid uid, Node node, out List<PowerMonitoringConsoleEntry> sources)
|
|
{
|
|
sources = new List<PowerMonitoringConsoleEntry>();
|
|
|
|
if (node.NodeGroup is not PowerNet netQ)
|
|
return;
|
|
|
|
var indexedSources = new Dictionary<EntityUid, PowerMonitoringConsoleEntry>();
|
|
var currentSupply = 0f;
|
|
var currentDemand = 0f;
|
|
|
|
foreach (var powerSupplier in netQ.Suppliers)
|
|
{
|
|
var ent = powerSupplier.Owner;
|
|
|
|
if (uid == ent)
|
|
continue;
|
|
|
|
currentSupply += powerSupplier.CurrentSupply;
|
|
|
|
if (TryComp<PowerMonitoringDeviceComponent>(ent, out var entDevice))
|
|
{
|
|
// Combine entities represented by an master into a single entry
|
|
if (entDevice.IsCollectionMasterOrChild && !entDevice.IsCollectionMaster)
|
|
ent = entDevice.CollectionMaster;
|
|
|
|
if (indexedSources.TryGetValue(ent, out var entry))
|
|
{
|
|
entry.PowerValue += powerSupplier.CurrentSupply;
|
|
indexedSources[ent] = entry;
|
|
|
|
continue;
|
|
}
|
|
|
|
indexedSources.Add(ent, new PowerMonitoringConsoleEntry(GetNetEntity(ent), entDevice.Group, powerSupplier.CurrentSupply, GetBatteryLevel(ent)));
|
|
}
|
|
}
|
|
|
|
foreach (var batteryDischarger in netQ.Dischargers)
|
|
{
|
|
var ent = batteryDischarger.Owner;
|
|
|
|
if (uid == ent)
|
|
continue;
|
|
|
|
if (!TryComp<PowerNetworkBatteryComponent>(ent, out var entBattery))
|
|
continue;
|
|
|
|
currentSupply += entBattery.CurrentSupply;
|
|
|
|
if (TryComp<PowerMonitoringDeviceComponent>(ent, out var entDevice))
|
|
{
|
|
// Combine entities represented by an master into a single entry
|
|
if (entDevice.IsCollectionMasterOrChild && !entDevice.IsCollectionMaster)
|
|
ent = entDevice.CollectionMaster;
|
|
|
|
if (indexedSources.TryGetValue(ent, out var entry))
|
|
{
|
|
entry.PowerValue += entBattery.CurrentSupply;
|
|
indexedSources[ent] = entry;
|
|
|
|
continue;
|
|
}
|
|
|
|
indexedSources.Add(ent, new PowerMonitoringConsoleEntry(GetNetEntity(ent), entDevice.Group, entBattery.CurrentSupply, GetBatteryLevel(ent)));
|
|
}
|
|
}
|
|
|
|
sources = indexedSources.Values.ToList();
|
|
|
|
// Get the total demand for the network
|
|
foreach (var powerConsumer in netQ.Consumers)
|
|
{
|
|
currentDemand += powerConsumer.ReceivedPower;
|
|
}
|
|
|
|
foreach (var batteryCharger in netQ.Chargers)
|
|
{
|
|
var ent = batteryCharger.Owner;
|
|
|
|
if (!TryComp<PowerNetworkBatteryComponent>(ent, out var entBattery))
|
|
continue;
|
|
|
|
currentDemand += entBattery.CurrentReceiving;
|
|
}
|
|
|
|
// Exit if supply / demand is negligible
|
|
if (MathHelper.CloseTo(currentDemand, 0) || MathHelper.CloseTo(currentSupply, 0))
|
|
return;
|
|
|
|
// Work out how much power this device (and those it represents) is actually receiving
|
|
if (!TryComp<PowerNetworkBatteryComponent>(uid, out var battery))
|
|
return;
|
|
|
|
var powerUsage = battery.CurrentReceiving;
|
|
|
|
if (TryComp<PowerMonitoringDeviceComponent>(uid, out var device) && device.IsCollectionMaster)
|
|
{
|
|
foreach ((var child, var _) in device.ChildDevices)
|
|
{
|
|
if (TryComp<PowerNetworkBatteryComponent>(child, out var childBattery))
|
|
powerUsage += childBattery.CurrentReceiving;
|
|
}
|
|
}
|
|
|
|
// Update the power value for each source based on the fraction of power the entity is actually draining from each
|
|
var powerFraction = Math.Min(powerUsage / currentSupply, 1f) * Math.Min(currentSupply / currentDemand, 1f);
|
|
|
|
for (int i = 0; i < sources.Count; i++)
|
|
{
|
|
var entry = sources[i];
|
|
sources[i] = new PowerMonitoringConsoleEntry(entry.NetEntity, entry.Group, entry.PowerValue * powerFraction, entry.BatteryLevel);
|
|
}
|
|
}
|
|
|
|
private void GetLoadsForNode(EntityUid uid, Node node, out List<PowerMonitoringConsoleEntry> loads, List<EntityUid>? children = null)
|
|
{
|
|
loads = new List<PowerMonitoringConsoleEntry>();
|
|
|
|
if (node.NodeGroup is not PowerNet netQ)
|
|
return;
|
|
|
|
var indexedLoads = new Dictionary<EntityUid, PowerMonitoringConsoleEntry>();
|
|
var currentDemand = 0f;
|
|
|
|
foreach (var powerConsumer in netQ.Consumers)
|
|
{
|
|
var ent = powerConsumer.Owner;
|
|
|
|
if (uid == ent)
|
|
continue;
|
|
|
|
currentDemand += powerConsumer.ReceivedPower;
|
|
|
|
if (TryComp<PowerMonitoringDeviceComponent>(ent, out var entDevice))
|
|
{
|
|
// Combine entities represented by an master into a single entry
|
|
if (entDevice.IsCollectionMasterOrChild && !entDevice.IsCollectionMaster)
|
|
ent = entDevice.CollectionMaster;
|
|
|
|
if (indexedLoads.TryGetValue(ent, out var entry))
|
|
{
|
|
entry.PowerValue += powerConsumer.ReceivedPower;
|
|
indexedLoads[ent] = entry;
|
|
|
|
continue;
|
|
}
|
|
|
|
indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(GetNetEntity(ent), entDevice.Group, powerConsumer.ReceivedPower, GetBatteryLevel(ent)));
|
|
}
|
|
}
|
|
|
|
foreach (var batteryCharger in netQ.Chargers)
|
|
{
|
|
var ent = batteryCharger.Owner;
|
|
|
|
if (uid == ent)
|
|
continue;
|
|
|
|
if (!TryComp<PowerNetworkBatteryComponent>(ent, out var battery))
|
|
continue;
|
|
|
|
currentDemand += battery.CurrentReceiving;
|
|
|
|
if (TryComp<PowerMonitoringDeviceComponent>(ent, out var entDevice))
|
|
{
|
|
// Combine entities represented by an master into a single entry
|
|
if (entDevice.IsCollectionMasterOrChild && !entDevice.IsCollectionMaster)
|
|
ent = entDevice.CollectionMaster;
|
|
|
|
if (indexedLoads.TryGetValue(ent, out var entry))
|
|
{
|
|
entry.PowerValue += battery.CurrentReceiving;
|
|
indexedLoads[ent] = entry;
|
|
|
|
continue;
|
|
}
|
|
|
|
indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(GetNetEntity(ent), entDevice.Group, battery.CurrentReceiving, GetBatteryLevel(ent)));
|
|
}
|
|
}
|
|
|
|
loads = indexedLoads.Values.ToList();
|
|
|
|
// Exit if demand is negligible
|
|
if (MathHelper.CloseTo(currentDemand, 0))
|
|
return;
|
|
|
|
var supplying = 0f;
|
|
|
|
// Work out how much power this device (and those it represents) is actually supplying
|
|
if (TryComp<PowerNetworkBatteryComponent>(uid, out var entBattery))
|
|
supplying = entBattery.CurrentSupply;
|
|
|
|
else if (TryComp<PowerSupplierComponent>(uid, out var entSupplier))
|
|
supplying = entSupplier.CurrentSupply;
|
|
|
|
if (TryComp<PowerMonitoringDeviceComponent>(uid, out var device) && device.IsCollectionMaster)
|
|
{
|
|
foreach ((var child, var _) in device.ChildDevices)
|
|
{
|
|
if (TryComp<PowerNetworkBatteryComponent>(child, out var childBattery))
|
|
supplying += childBattery.CurrentSupply;
|
|
|
|
else if (TryComp<PowerSupplierComponent>(child, out var childSupplier))
|
|
supplying += childSupplier.CurrentSupply;
|
|
}
|
|
}
|
|
|
|
// Update the power value for each load based on the fraction of power these entities are actually draining from this device
|
|
var powerFraction = Math.Min(supplying / currentDemand, 1f);
|
|
|
|
for (int i = 0; i < indexedLoads.Values.Count; i++)
|
|
{
|
|
var entry = loads[i];
|
|
loads[i] = new PowerMonitoringConsoleEntry(entry.NetEntity, entry.Group, entry.PowerValue * powerFraction, entry.BatteryLevel);
|
|
}
|
|
}
|
|
|
|
// Designates a supplied entity as a 'collection master'. Other entities which share this
|
|
// entities collection name and are attached on the same load network are assigned this entity
|
|
// as the master that represents them on the console UI. This way you can have one device
|
|
// represent multiple connected devices
|
|
private void AssignEntityAsCollectionMaster
|
|
(EntityUid uid,
|
|
PowerMonitoringDeviceComponent? device = null,
|
|
TransformComponent? xform = null,
|
|
NodeContainerComponent? nodeContainer = null)
|
|
{
|
|
if (!Resolve(uid, ref device, ref nodeContainer, ref xform, false))
|
|
return;
|
|
|
|
// If the device is not attached to a network, exit
|
|
var nodeName = device.SourceNode == string.Empty ? device.LoadNode : device.SourceNode;
|
|
|
|
if (!nodeContainer.Nodes.TryGetValue(nodeName, out var node) ||
|
|
node.ReachableNodes.Count == 0)
|
|
{
|
|
// Make a child the new master of the collection if necessary
|
|
if (device.ChildDevices.TryFirstOrNull(out var kvp))
|
|
{
|
|
var newMaster = kvp.Value.Key;
|
|
var newMasterDevice = kvp.Value.Value;
|
|
|
|
newMasterDevice.CollectionMaster = newMaster;
|
|
newMasterDevice.ChildDevices.Clear();
|
|
|
|
foreach ((var child, var childDevice) in device.ChildDevices)
|
|
{
|
|
newMasterDevice.ChildDevices.Add(child, childDevice);
|
|
|
|
childDevice.CollectionMaster = newMaster;
|
|
UpdateCollectionChildMetaData(child, newMaster);
|
|
}
|
|
|
|
UpdateCollectionMasterMetaData(newMaster, newMasterDevice.ChildDevices.Count);
|
|
}
|
|
|
|
device.CollectionMaster = uid;
|
|
device.ChildDevices.Clear();
|
|
UpdateCollectionMasterMetaData(uid, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
// Check to see if the device has a valid existing master
|
|
if (!device.IsCollectionMaster &&
|
|
device.CollectionMaster.IsValid() &&
|
|
TryComp<NodeContainerComponent>(device.CollectionMaster, out var masterNodeContainer) &&
|
|
DevicesHaveMatchingNodes(nodeContainer, masterNodeContainer))
|
|
return;
|
|
|
|
// If not, make this a new master
|
|
device.CollectionMaster = uid;
|
|
device.ChildDevices.Clear();
|
|
|
|
// Search for children
|
|
var query = AllEntityQuery<PowerMonitoringDeviceComponent, TransformComponent, NodeContainerComponent>();
|
|
while (query.MoveNext(out var ent, out var entDevice, out var entXform, out var entNodeContainer))
|
|
{
|
|
if (entDevice.CollectionName != device.CollectionName)
|
|
continue;
|
|
|
|
if (ent == uid)
|
|
continue;
|
|
|
|
if (entXform.GridUid != xform.GridUid)
|
|
continue;
|
|
|
|
if (!DevicesHaveMatchingNodes(nodeContainer, entNodeContainer))
|
|
continue;
|
|
|
|
device.ChildDevices.Add(ent, entDevice);
|
|
|
|
entDevice.CollectionMaster = uid;
|
|
UpdateCollectionChildMetaData(ent, uid);
|
|
}
|
|
|
|
UpdateCollectionMasterMetaData(uid, device.ChildDevices.Count);
|
|
}
|
|
|
|
private bool DevicesHaveMatchingNodes(NodeContainerComponent nodeContainerA, NodeContainerComponent nodeContainerB)
|
|
{
|
|
foreach ((var key, var nodeA) in nodeContainerA.Nodes)
|
|
{
|
|
if (!nodeContainerB.Nodes.TryGetValue(key, out var nodeB))
|
|
return false;
|
|
|
|
if (nodeA.NodeGroup != nodeB.NodeGroup)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void UpdateCollectionChildMetaData(EntityUid child, EntityUid master)
|
|
{
|
|
var netEntity = GetNetEntity(child);
|
|
var xform = Transform(child);
|
|
|
|
var query = AllEntityQuery<PowerMonitoringConsoleComponent, TransformComponent>();
|
|
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
|
|
{
|
|
if (entXform.GridUid != xform.GridUid)
|
|
continue;
|
|
|
|
if (!entConsole.PowerMonitoringDeviceMetaData.TryGetValue(netEntity, out var metaData))
|
|
continue;
|
|
|
|
metaData.CollectionMaster = GetNetEntity(master);
|
|
entConsole.PowerMonitoringDeviceMetaData[netEntity] = metaData;
|
|
|
|
Dirty(ent, entConsole);
|
|
}
|
|
}
|
|
|
|
private void UpdateCollectionMasterMetaData(EntityUid master, int childCount)
|
|
{
|
|
var netEntity = GetNetEntity(master);
|
|
var xform = Transform(master);
|
|
|
|
var query = AllEntityQuery<PowerMonitoringConsoleComponent, TransformComponent>();
|
|
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
|
|
{
|
|
if (entXform.GridUid != xform.GridUid)
|
|
continue;
|
|
|
|
if (!entConsole.PowerMonitoringDeviceMetaData.TryGetValue(netEntity, out var metaData))
|
|
continue;
|
|
|
|
if (childCount > 0)
|
|
{
|
|
var name = MetaData(master).EntityPrototype?.Name ?? MetaData(master).EntityName;
|
|
metaData.EntityName = Loc.GetString("power-monitoring-window-object-array", ("name", name), ("count", childCount + 1));
|
|
}
|
|
|
|
else
|
|
{
|
|
metaData.EntityName = MetaData(master).EntityName;
|
|
}
|
|
|
|
metaData.CollectionMaster = null;
|
|
entConsole.PowerMonitoringDeviceMetaData[netEntity] = metaData;
|
|
|
|
Dirty(ent, entConsole);
|
|
}
|
|
}
|
|
|
|
private Dictionary<Vector2i, PowerCableChunk> RefreshPowerCableGrid(EntityUid gridUid, MapGridComponent grid)
|
|
{
|
|
// Clears all chunks for the associated grid
|
|
var allChunks = new Dictionary<Vector2i, PowerCableChunk>();
|
|
_gridPowerCableChunks[gridUid] = allChunks;
|
|
|
|
// Adds all power cables to the grid
|
|
var query = AllEntityQuery<CableComponent, TransformComponent>();
|
|
while (query.MoveNext(out var ent, out var cable, out var entXform))
|
|
{
|
|
if (entXform.GridUid != gridUid)
|
|
continue;
|
|
|
|
var tile = _sharedMapSystem.GetTileRef(gridUid, grid, entXform.Coordinates);
|
|
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile.GridIndices, ChunkSize);
|
|
|
|
if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
|
|
{
|
|
chunk = new PowerCableChunk(chunkOrigin);
|
|
allChunks[chunkOrigin] = chunk;
|
|
}
|
|
|
|
var relative = SharedMapSystem.GetChunkRelative(tile.GridIndices, ChunkSize);
|
|
var flag = GetFlag(relative);
|
|
|
|
chunk.PowerCableData[(int) cable.CableType] |= flag;
|
|
}
|
|
|
|
return allChunks;
|
|
}
|
|
|
|
private void UpdateFocusNetwork(EntityUid uid, PowerMonitoringCableNetworksComponent component, EntityUid gridUid, MapGridComponent grid, List<EntityUid> nodeList)
|
|
{
|
|
component.FocusChunks.Clear();
|
|
|
|
foreach (var ent in nodeList)
|
|
{
|
|
var xform = Transform(ent);
|
|
var tile = _sharedMapSystem.GetTileRef(gridUid, grid, xform.Coordinates);
|
|
var gridIndices = tile.GridIndices;
|
|
var chunkOrigin = SharedMapSystem.GetChunkIndices(gridIndices, ChunkSize);
|
|
|
|
if (!component.FocusChunks.TryGetValue(chunkOrigin, out var chunk))
|
|
{
|
|
chunk = new PowerCableChunk(chunkOrigin);
|
|
component.FocusChunks[chunkOrigin] = chunk;
|
|
}
|
|
|
|
var relative = SharedMapSystem.GetChunkRelative(gridIndices, ChunkSize);
|
|
var flag = GetFlag(relative);
|
|
|
|
if (TryComp<CableComponent>(ent, out var cable))
|
|
chunk.PowerCableData[(int) cable.CableType] |= flag;
|
|
}
|
|
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
private void RefreshPowerMonitoringConsole(EntityUid uid, PowerMonitoringConsoleComponent component)
|
|
{
|
|
component.Focus = null;
|
|
component.FocusGroup = PowerMonitoringConsoleGroup.Generator;
|
|
component.PowerMonitoringDeviceMetaData.Clear();
|
|
component.Flags = 0;
|
|
|
|
var xform = Transform(uid);
|
|
|
|
if (xform.GridUid == null)
|
|
return;
|
|
|
|
var grid = xform.GridUid.Value;
|
|
|
|
var query = AllEntityQuery<PowerMonitoringDeviceComponent, TransformComponent>();
|
|
while (query.MoveNext(out var ent, out var entDevice, out var entXform))
|
|
{
|
|
if (grid != entXform.GridUid)
|
|
continue;
|
|
|
|
var netEntity = GetNetEntity(ent);
|
|
var name = MetaData(ent).EntityName;
|
|
var netCoords = GetNetCoordinates(entXform.Coordinates);
|
|
|
|
var metaData = new PowerMonitoringDeviceMetaData(name, netCoords, entDevice.Group, entDevice.SpritePath, entDevice.SpriteState);
|
|
|
|
if (entDevice.IsCollectionMasterOrChild)
|
|
{
|
|
if (!entDevice.IsCollectionMaster)
|
|
{
|
|
metaData.CollectionMaster = GetNetEntity(entDevice.CollectionMaster);
|
|
}
|
|
|
|
else if (entDevice.ChildDevices.Count > 0)
|
|
{
|
|
name = MetaData(ent).EntityPrototype?.Name ?? MetaData(ent).EntityName;
|
|
metaData.EntityName = Loc.GetString("power-monitoring-window-object-array", ("name", name), ("count", entDevice.ChildDevices.Count + 1));
|
|
}
|
|
}
|
|
|
|
component.PowerMonitoringDeviceMetaData.Add(netEntity, metaData);
|
|
}
|
|
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
private void RefreshPowerMonitoringCableNetworks(EntityUid uid, PowerMonitoringCableNetworksComponent component)
|
|
{
|
|
var xform = Transform(uid);
|
|
|
|
if (xform.GridUid == null)
|
|
return;
|
|
|
|
var grid = xform.GridUid.Value;
|
|
|
|
if (!TryComp<MapGridComponent>(grid, out var map))
|
|
return;
|
|
|
|
if (!_gridPowerCableChunks.TryGetValue(grid, out var allChunks))
|
|
allChunks = RefreshPowerCableGrid(grid, map);
|
|
|
|
component.AllChunks = allChunks;
|
|
component.FocusChunks.Clear();
|
|
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
private struct PowerStats
|
|
{
|
|
public double PowerValue { get; set; }
|
|
public double PowerSupplied { get; set; }
|
|
public double PowerUsage { get; set; }
|
|
public double BatteryUsage { get; set; }
|
|
public float? BatteryLevel { get; set; }
|
|
}
|
|
}
|