diff --git a/Content.Client/Power/Generator/GeneratorWindow.xaml.cs b/Content.Client/Power/Generator/GeneratorWindow.xaml.cs index d1c5f47b07..bd5b75de1d 100644 --- a/Content.Client/Power/Generator/GeneratorWindow.xaml.cs +++ b/Content.Client/Power/Generator/GeneratorWindow.xaml.cs @@ -135,6 +135,14 @@ public sealed partial class GeneratorWindow : FancyWindow private bool TryGetStartProgress(out float progress) { + // Try to check progress of auto-revving first + if (_entityManager.TryGetComponent(_entity, out var activeGeneratorRevvingComponent) && _entityManager.TryGetComponent(_entity, out var portableGeneratorComponent)) + { + var calculatedProgress = activeGeneratorRevvingComponent.CurrentTime / portableGeneratorComponent.StartTime; + progress = (float) calculatedProgress; + return true; + } + var doAfterSystem = _entityManager.EntitySysManager.GetEntitySystem(); return doAfterSystem.TryFindActiveDoAfter(_entity, out _, out _, out progress); } diff --git a/Content.Server/Power/Generator/GeneratorSignalControlComponent.cs b/Content.Server/Power/Generator/GeneratorSignalControlComponent.cs new file mode 100644 index 0000000000..f16a09eae3 --- /dev/null +++ b/Content.Server/Power/Generator/GeneratorSignalControlComponent.cs @@ -0,0 +1,29 @@ +using Content.Shared.DeviceLinking; +using Robust.Shared.Prototypes; + +namespace Content.Server.Power.Generator; + +/// +/// When attached to an entity with it will allow the signal network to exert control over the generator. +/// +[RegisterComponent] +public sealed partial class GeneratorSignalControlComponent: Component +{ + /// + /// The port that should be invoked when turning the generator on. + /// + [DataField] + public ProtoId OnPort = "On"; + + /// + /// The port that should be invoked when turning the generator off. + /// + [DataField] + public ProtoId OffPort = "Off"; + + /// + /// The port that should be invoked when toggling the generator. + /// + [DataField] + public ProtoId TogglePort = "Toggle"; +} diff --git a/Content.Server/Power/Generator/GeneratorSignalControlSystem.cs b/Content.Server/Power/Generator/GeneratorSignalControlSystem.cs new file mode 100644 index 0000000000..4a42bcfffb --- /dev/null +++ b/Content.Server/Power/Generator/GeneratorSignalControlSystem.cs @@ -0,0 +1,48 @@ +using System.ComponentModel; +using Content.Server.DeviceLinking.Events; +using Content.Shared.Power.Generator; + +namespace Content.Server.Power.Generator; + +public sealed class GeneratorSignalControlSystem: EntitySystem +{ + [Dependency] private readonly GeneratorSystem _generator = default!; + [Dependency] private readonly ActiveGeneratorRevvingSystem _revving = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnSignalReceived); + } + + /// + /// Change the state of the generator depending on what signal is sent. + /// + private void OnSignalReceived(EntityUid uid, GeneratorSignalControlComponent component, SignalReceivedEvent args) + { + if (!TryComp(uid, out var generator)) + return; + + if (args.Port == component.OnPort) + { + _revving.StartAutoRevving(uid); + } + else if (args.Port == component.OffPort) + { + _generator.SetFuelGeneratorOn(uid, false, generator); + _revving.StopAutoRevving(uid); + } + else if (args.Port == component.TogglePort) + { + if (generator.On) + { + _generator.SetFuelGeneratorOn(uid, false, generator); + _revving.StopAutoRevving(uid); + } + else + { + _revving.StartAutoRevving(uid); + } + } + } +} diff --git a/Content.Server/Power/Generator/PortableGeneratorSystem.cs b/Content.Server/Power/Generator/PortableGeneratorSystem.cs index 96016da5b0..e8e9c5b45e 100644 --- a/Content.Server/Power/Generator/PortableGeneratorSystem.cs +++ b/Content.Server/Power/Generator/PortableGeneratorSystem.cs @@ -27,6 +27,7 @@ public sealed class PortableGeneratorSystem : SharedPortableGeneratorSystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly GeneratorSystem _generator = default!; [Dependency] private readonly PowerSwitchableSystem _switchable = default!; + [Dependency] private readonly ActiveGeneratorRevvingSystem _revving = default!; [Dependency] private readonly PowerNetSystem _powerNet = default!; public override void Initialize() @@ -38,7 +39,8 @@ public sealed class PortableGeneratorSystem : SharedPortableGeneratorSystem UpdatesAfter.Add(typeof(PowerNetSystem)); SubscribeLocalEvent>(GetAlternativeVerb); - SubscribeLocalEvent(GeneratorTugged); + SubscribeLocalEvent(OnGeneratorStarted); + SubscribeLocalEvent(OnAutoGeneratorStarted); SubscribeLocalEvent(GeneratorStartMessage); SubscribeLocalEvent(GeneratorStopMessage); SubscribeLocalEvent(GeneratorSwitchOutputMessage); @@ -92,9 +94,30 @@ public sealed class PortableGeneratorSystem : SharedPortableGeneratorSystem _generator.SetFuelGeneratorOn(uid, false); } - private void GeneratorTugged(EntityUid uid, PortableGeneratorComponent component, GeneratorStartedEvent args) + private void OnGeneratorStarted(EntityUid uid, PortableGeneratorComponent component, GeneratorStartedEvent args) { - if (args.Cancelled || !Transform(uid).Anchored) + if (args.Cancelled) + return; + + GeneratorTugged(uid, component, args.User, out args.Repeat); + } + + private void OnAutoGeneratorStarted(EntityUid uid, PortableGeneratorComponent component, ref AutoGeneratorStartedEvent args) + { + GeneratorTugged(uid, component, null, out var repeat); + + // restart the auto rev if it should be repeated + if (repeat) + _revving.StartAutoRevving(uid); + else + args.Started = true; + } + + private void GeneratorTugged(EntityUid uid, PortableGeneratorComponent component, EntityUid? user, out bool repeat) + { + repeat = false; + + if (!Transform(uid).Anchored) return; var fuelGenerator = Comp(uid); @@ -107,14 +130,23 @@ public sealed class PortableGeneratorSystem : SharedPortableGeneratorSystem if (!clogged && !empty && _random.Prob(component.StartChance)) { - _popup.PopupEntity(Loc.GetString("portable-generator-start-success"), uid, args.User); _generator.SetFuelGeneratorOn(uid, true, fuelGenerator); + + if (user is null) + return; + + _popup.PopupEntity(Loc.GetString("portable-generator-start-success"), uid, user.Value); + } else { - _popup.PopupEntity(Loc.GetString("portable-generator-start-fail"), uid, args.User); - // Try again bozo - args.Repeat = true; + // try again bozo + repeat = true; + + if (user is null) + return; + + _popup.PopupEntity(Loc.GetString("portable-generator-start-fail"), uid, user.Value); } } diff --git a/Content.Shared/Power/Generator/ActiveGeneratorRevvingComponent.cs b/Content.Shared/Power/Generator/ActiveGeneratorRevvingComponent.cs new file mode 100644 index 0000000000..25f97dc15a --- /dev/null +++ b/Content.Shared/Power/Generator/ActiveGeneratorRevvingComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Power.Generator; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ActiveGeneratorRevvingComponent: Component +{ + [DataField, ViewVariables(VVAccess.ReadOnly), AutoNetworkedField] + public TimeSpan CurrentTime = TimeSpan.Zero; +} diff --git a/Content.Shared/Power/Generator/ActiveGeneratorRevvingSystem.cs b/Content.Shared/Power/Generator/ActiveGeneratorRevvingSystem.cs new file mode 100644 index 0000000000..1c39997f30 --- /dev/null +++ b/Content.Shared/Power/Generator/ActiveGeneratorRevvingSystem.cs @@ -0,0 +1,86 @@ +using Content.Shared.DoAfter; + +namespace Content.Shared.Power.Generator; + +public sealed class ActiveGeneratorRevvingSystem: EntitySystem +{ + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly EntityManager _entity = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnAnchorStateChanged); + } + + /// + /// Handles the AnchorStateChangedEvent to stop auto-revving when unanchored. + /// + private void OnAnchorStateChanged(EntityUid uid, ActiveGeneratorRevvingComponent component, AnchorStateChangedEvent args) + { + if (!args.Anchored) + StopAutoRevving(uid); + } + + /// + /// Start revving a generator entity automatically, without another entity doing a do-after. + /// Used for remotely activating a generator. + /// + /// Uid of the generator entity. + /// ActiveGeneratorRevvingComponent of the generator entity. + public void StartAutoRevving(EntityUid uid, ActiveGeneratorRevvingComponent? component = null) + { + if (Resolve(uid, ref component)) + { + // reset the revving + component.CurrentTime = TimeSpan.FromSeconds(0); + return; + } + + AddComp(uid, new ActiveGeneratorRevvingComponent()); + } + + /// + /// Stop revving a generator entity. + /// + /// Uid of the generator entity. + /// True if the auto-revving was cancelled, false if it was never revving in the first place. + public bool StopAutoRevving(EntityUid uid) + { + return RemComp(uid); + } + + /// + /// Raise an event on a generator entity to start it. + /// + /// This is not the same as revving it, when this is called the generator will start producing power. + /// Uid of the generator entity. + /// True if the generator was successfully started, false otherwise. + private bool StartGenerator(EntityUid uid) + { + var ev = new AutoGeneratorStartedEvent(); + RaiseLocalEvent(uid, ref ev); + return ev.Started; + } + + /// + /// Updates the timers on ActiveGeneratorRevvingComponent(s), and stops them when they are finished. + /// + public override void Update(float frameTime) + { + base.Update(frameTime); + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var activeGeneratorRevvingComponent, out var portableGeneratorComponent)) + { + activeGeneratorRevvingComponent.CurrentTime += TimeSpan.FromSeconds(frameTime); + Dirty(uid, activeGeneratorRevvingComponent); + + if (activeGeneratorRevvingComponent.CurrentTime < portableGeneratorComponent.StartTime) + continue; + + if (StartGenerator(uid)) + StopAutoRevving(uid); + } + } +} diff --git a/Content.Shared/Power/Generator/SharedPortableGeneratorSystem.cs b/Content.Shared/Power/Generator/SharedPortableGeneratorSystem.cs index e30c8eced6..e8ccc85166 100644 --- a/Content.Shared/Power/Generator/SharedPortableGeneratorSystem.cs +++ b/Content.Shared/Power/Generator/SharedPortableGeneratorSystem.cs @@ -23,3 +23,12 @@ public sealed partial class GeneratorStartedEvent : DoAfterEvent return this; } } + +/// +/// Used to start a portable generator. This is like except it isn't a do-after. +/// +[ByRefEvent] +public sealed partial class AutoGeneratorStartedEvent +{ + public bool Started = false; +} diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml index 2fc6c1f512..b606c01f1d 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml @@ -90,6 +90,17 @@ supplyRampRate: 500 supplyRampTolerance: 500 enabled: false + - type: DeviceLinkSink + ports: + - On + - Off + - Toggle + - type: GeneratorSignalControl + - type: DeviceNetwork + deviceNetId: Wireless + receiveFrequencyId: BasicDevice + - type: WirelessNetworkConnection + range: 200 - type: entity abstract: true