mirror of
https://github.com/wega-team/ss14-wega.git
synced 2026-02-14 19:30:01 +01:00
Basic Dynamic Power Consumption Systems (#41885)
* init commit * Addr reviews
This commit is contained in:
186
Content.IntegrationTests/Tests/Power/PowerStateTest.cs
Normal file
186
Content.IntegrationTests/Tests/Power/PowerStateTest.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using Content.Shared.Coordinates;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Power;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class PowerStateTest
|
||||
{
|
||||
[TestPrototypes]
|
||||
private const string Prototypes = @"
|
||||
- type: entity
|
||||
id: PowerStateApcReceiverDummy
|
||||
components:
|
||||
- type: ApcPowerReceiver
|
||||
- type: ExtensionCableReceiver
|
||||
- type: Transform
|
||||
anchored: true
|
||||
- type: PowerState
|
||||
isWorking: false
|
||||
idlePowerDraw: 10
|
||||
workingPowerDraw: 50
|
||||
";
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that switching from idle to working updates the power receiver load to the working draw.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SetWorkingState_IdleToWorking_UpdatesLoad()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapSys = entManager.System<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
mapSys.CreateMap(out var mapId);
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
|
||||
mapSys.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
|
||||
var ent = entManager.SpawnEntity("PowerStateApcReceiverDummy", grid.Owner.ToCoordinates());
|
||||
|
||||
var receiver = entManager.GetComponent<Server.Power.Components.ApcPowerReceiverComponent>(ent);
|
||||
var powerState = entManager.GetComponent<PowerStateComponent>(ent);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.False);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
var system = entManager.System<PowerStateSystem>();
|
||||
system.SetWorkingState((ent, powerState), true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.True);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f));
|
||||
});
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that switching from working to idle updates the power receiver load to the idle draw.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SetWorkingState_WorkingToIdle_UpdatesLoad()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapSys = entManager.System<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
mapSys.CreateMap(out var mapId);
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
|
||||
mapSys.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
|
||||
var ent = entManager.SpawnEntity("PowerStateApcReceiverDummy", grid.Owner.ToCoordinates());
|
||||
|
||||
var receiver = entManager.GetComponent<Server.Power.Components.ApcPowerReceiverComponent>(ent);
|
||||
var powerState = entManager.GetComponent<PowerStateComponent>(ent);
|
||||
var system = entManager.System<PowerStateSystem>();
|
||||
Entity<PowerStateComponent> newEnt = (ent, powerState);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.False);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
system.SetWorkingState(newEnt, true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.True);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
system.SetWorkingState(newEnt, false);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.False);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f));
|
||||
});
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that setting the working state to the current state does not change the power receiver load.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SetWorkingState_AlreadyInState_NoChange()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapSys = entManager.System<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
mapSys.CreateMap(out var mapId);
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
|
||||
mapSys.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
|
||||
var ent = entManager.SpawnEntity("PowerStateApcReceiverDummy", grid.Owner.ToCoordinates());
|
||||
|
||||
var receiver = entManager.GetComponent<Server.Power.Components.ApcPowerReceiverComponent>(ent);
|
||||
var powerState = entManager.GetComponent<PowerStateComponent>(ent);
|
||||
var system = entManager.System<PowerStateSystem>();
|
||||
Entity<PowerStateComponent> valueTuple = (ent, powerState);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.False);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
system.SetWorkingState(valueTuple, false);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.False);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
system.SetWorkingState(valueTuple, true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.True);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
system.SetWorkingState(valueTuple, true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.True);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f));
|
||||
});
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
|
||||
32
Content.Shared/Power/Components/PowerStateComponent.cs
Normal file
32
Content.Shared/Power/Components/PowerStateComponent.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Content.Shared.Power.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Generic component for giving entities "idle" and "working" power states.
|
||||
/// </summary>
|
||||
/// <remarks><para>Entities that have more complex power draw
|
||||
/// (ex. a thermomachine whose heating power is directly tied to its power consumption)
|
||||
/// should just directly set their load on the <see cref="SharedApcPowerReceiverComponent"/>.</para>
|
||||
///
|
||||
/// <para>This is also applicable if you would like to add
|
||||
/// more complex power behavior that is tied to a generic component.</para></remarks>
|
||||
[RegisterComponent]
|
||||
public sealed partial class PowerStateComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the entity is currently in the working state.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool IsWorking;
|
||||
|
||||
/// <summary>
|
||||
/// The idle power draw of this entity when not working, in watts.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float IdlePowerDraw = 20f;
|
||||
|
||||
/// <summary>
|
||||
/// The working power draw of this entity when working, in watts.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float WorkingPowerDraw = 350f;
|
||||
}
|
||||
16
Content.Shared/Power/Components/UIPowerStateComponent.cs
Normal file
16
Content.Shared/Power/Components/UIPowerStateComponent.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Content.Shared.Power.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Component for entities that want to increase their power usage to a working state when
|
||||
/// a UI on the machine is open. Requires <see cref="PowerStateComponent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class UIPowerStateComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// List of UI keys that will trigger the working state.
|
||||
/// If null, any UI open will trigger the working state.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<Enum>? Keys;
|
||||
}
|
||||
44
Content.Shared/Power/EntitySystems/PowerStateSystem.cs
Normal file
44
Content.Shared/Power/EntitySystems/PowerStateSystem.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Content.Shared.Power.Components;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Shared.Power.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Generic system that handles entities with <see cref="PowerStateComponent"/>.
|
||||
/// Used for simple machines that only need to switch between "idle" and "working" power states.
|
||||
/// </summary>
|
||||
public sealed class PowerStateSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedPowerReceiverSystem _powerReceiverSystem = default!;
|
||||
|
||||
private EntityQuery<PowerStateComponent> _powerStateQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PowerStateComponent, ComponentStartup>(OnComponentStartup);
|
||||
|
||||
_powerStateQuery = GetEntityQuery<PowerStateComponent>();
|
||||
}
|
||||
|
||||
private void OnComponentStartup(Entity<PowerStateComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
SetWorkingState(ent.Owner, ent.Comp.IsWorking);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the working state of the entity, adjusting its power draw accordingly.
|
||||
/// </summary>
|
||||
/// <param name="ent">The entity to set the working state for.</param>
|
||||
/// <param name="working">Whether the entity should be in the working state.</param>
|
||||
[PublicAPI]
|
||||
public void SetWorkingState(Entity<PowerStateComponent?> ent, bool working)
|
||||
{
|
||||
if (!_powerStateQuery.Resolve(ent, ref ent.Comp))
|
||||
return;
|
||||
|
||||
_powerReceiverSystem.SetLoad(ent.Owner, working ? ent.Comp.WorkingPowerDraw : ent.Comp.IdlePowerDraw);
|
||||
ent.Comp.IsWorking = working;
|
||||
}
|
||||
}
|
||||
46
Content.Shared/Power/EntitySystems/UIPowerStateSystem.cs
Normal file
46
Content.Shared/Power/EntitySystems/UIPowerStateSystem.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Content.Shared.Power.Components;
|
||||
|
||||
namespace Content.Shared.Power.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// System for entities with <see cref="UIPowerStateComponent"/>.
|
||||
/// Entities with this component will increase their power usage to a working state
|
||||
/// when a UI on the entity is open.
|
||||
/// </summary>
|
||||
public sealed class UIPowerStateSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly PowerStateSystem _powerState = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<UIPowerStateComponent, BoundUIOpenedEvent>(OnUiOpened);
|
||||
SubscribeLocalEvent<UIPowerStateComponent, BoundUIClosedEvent>(OnUiClosed);
|
||||
}
|
||||
|
||||
private void OnUiClosed(Entity<UIPowerStateComponent> ent, ref BoundUIClosedEvent args)
|
||||
{
|
||||
if (ent.Comp.Keys is null)
|
||||
{
|
||||
if (_ui.IsAnyUiOpen(ent.Owner))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_ui.IsUiOpen(ent.Owner, ent.Comp.Keys))
|
||||
return;
|
||||
}
|
||||
|
||||
_powerState.SetWorkingState(ent.Owner, false);
|
||||
}
|
||||
|
||||
private void OnUiOpened(Entity<UIPowerStateComponent> ent, ref BoundUIOpenedEvent args)
|
||||
{
|
||||
if (ent.Comp.Keys is not null && !ent.Comp.Keys.Contains(args.UiKey))
|
||||
return;
|
||||
|
||||
_powerState.SetWorkingState(ent.Owner, true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user