diff --git a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs index c41f67173d..734def3653 100644 --- a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs +++ b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs @@ -1,6 +1,5 @@ using Content.Shared.Singularity.Components; using Robust.Client.GameObjects; -using Robust.Shared.GameObjects; namespace Content.Client.ParticleAccelerator.UI { diff --git a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs index 0b916c7600..0df9363cb8 100644 --- a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs +++ b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs @@ -19,10 +19,10 @@ namespace Content.Client.ParticleAccelerator.UI { private readonly ShaderInstance _greyScaleShader; - private readonly ParticleAcceleratorBoundUserInterface Owner; + private readonly ParticleAcceleratorBoundUserInterface _owner; private readonly Label _drawLabel; - private readonly NoiseGenerator _drawNoiseGenerator; + private readonly FastNoiseLite _drawNoiseGenerator; private readonly Button _onButton; private readonly Button _offButton; private readonly Button _scanButton; @@ -36,9 +36,9 @@ namespace Content.Client.ParticleAccelerator.UI private readonly PASegmentControl _fuelChamberTexture; private readonly PASegmentControl _controlBoxTexture; private readonly PASegmentControl _powerBoxTexture; - private readonly PASegmentControl _emitterCenterTexture; - private readonly PASegmentControl _emitterRightTexture; - private readonly PASegmentControl _emitterLeftTexture; + private readonly PASegmentControl _emitterForeTexture; + private readonly PASegmentControl _emitterPortTexture; + private readonly PASegmentControl _emitterStarboardTexture; private float _time; private int _lastDraw; @@ -47,14 +47,16 @@ namespace Content.Client.ParticleAccelerator.UI private bool _blockSpinBox; private bool _assembled; private bool _shouldContinueAnimating; + private int _maxStrength = 3; public ParticleAcceleratorControlMenu(ParticleAcceleratorBoundUserInterface owner) { SetSize = (400, 320); _greyScaleShader = IoCManager.Resolve().Index("Greyscale").Instance(); - Owner = owner; - _drawNoiseGenerator = new NoiseGenerator(NoiseGenerator.NoiseType.Fbm); + _owner = owner; + _drawNoiseGenerator = new(); + _drawNoiseGenerator.SetFractalType(FastNoiseLite.FractalType.FBm); _drawNoiseGenerator.SetFrequency(0.5f); var resourceCache = IoCManager.Resolve(); @@ -98,7 +100,7 @@ namespace Content.Client.ParticleAccelerator.UI MouseFilter = MouseFilterMode.Pass }); - _stateSpinBox = new SpinBox {Value = 0, IsValid = StrengthSpinBoxValid,}; + _stateSpinBox = new SpinBox { Value = 0, IsValid = StrengthSpinBoxValid }; _stateSpinBox.InitDefaultButtons(); _stateSpinBox.ValueChanged += PowerStateChanged; _stateSpinBox.LineEditDisabled = true; @@ -107,7 +109,7 @@ namespace Content.Client.ParticleAccelerator.UI { ToggleMode = false, Text = Loc.GetString("particle-accelerator-control-menu-off-button"), - StyleClasses = {StyleBase.ButtonOpenRight}, + StyleClasses = { StyleBase.ButtonOpenRight }, }; _offButton.OnPressed += args => owner.SendEnableMessage(false); @@ -115,13 +117,13 @@ namespace Content.Client.ParticleAccelerator.UI { ToggleMode = false, Text = Loc.GetString("particle-accelerator-control-menu-on-button"), - StyleClasses = {StyleBase.ButtonOpenLeft}, + StyleClasses = { StyleBase.ButtonOpenLeft }, }; _onButton.OnPressed += args => owner.SendEnableMessage(true); var closeButton = new TextureButton { - StyleClasses = {"windowCloseButton"}, + StyleClasses = { "windowCloseButton" }, HorizontalAlignment = HAlignment.Right, Margin = new Thickness(0, 0, 8, 0) }; @@ -130,7 +132,7 @@ namespace Content.Client.ParticleAccelerator.UI var serviceManual = new Label { HorizontalAlignment = HAlignment.Center, - StyleClasses = {StyleBase.StyleClassLabelSubText}, + StyleClasses = { StyleBase.StyleClassLabelSubText }, Text = Loc.GetString("particle-accelerator-control-menu-service-manual-reference") }; _drawLabel = new Label(); @@ -227,7 +229,8 @@ namespace Content.Client.ParticleAccelerator.UI Align = Label.AlignMode.Center }, serviceManual - } + }, + Visible = false, }), } }, @@ -267,9 +270,9 @@ namespace Content.Client.ParticleAccelerator.UI new Control {MinSize = imgSize}, (_powerBoxTexture = Segment("power_box")), new Control {MinSize = imgSize}, - (_emitterLeftTexture = Segment("emitter_left")), - (_emitterCenterTexture = Segment("emitter_center")), - (_emitterRightTexture = Segment("emitter_right")), + (_emitterStarboardTexture = Segment("emitter_starboard")), + (_emitterForeTexture = Segment("emitter_fore")), + (_emitterPortTexture = Segment("emitter_port")), } } } @@ -312,7 +315,7 @@ namespace Content.Client.ParticleAccelerator.UI } }); - _scanButton.OnPressed += args => Owner.SendScanPartsMessage(); + _scanButton.OnPressed += args => _owner.SendScanPartsMessage(); _alarmControl.AnimationCompleted += s => { @@ -336,7 +339,7 @@ namespace Content.Client.ParticleAccelerator.UI private bool StrengthSpinBoxValid(int n) { - return (n >= 0 && n <= 3 && !_blockSpinBox); + return n >= 0 && n <= _maxStrength && !_blockSpinBox; } private void PowerStateChanged(ValueChangedEventArgs e) @@ -356,16 +359,15 @@ namespace Content.Client.ParticleAccelerator.UI case 3: newState = ParticleAcceleratorPowerState.Level2; break; - // They can't reach this level anyway and I just want to fix the bugginess for now. - //case 4: - // newState = ParticleAcceleratorPowerState.Level3; - // break; + case 4: + newState = ParticleAcceleratorPowerState.Level3; + break; default: return; } _stateSpinBox.SetButtonDisabled(true); - Owner.SendPowerStateMessage(newState); + _owner.SendPowerStateMessage(newState); } protected override DragMode GetDragModeFor(Vector2 relativeMousePos) @@ -402,14 +404,15 @@ namespace Content.Client.ParticleAccelerator.UI }); - _shouldContinueAnimating = false; - _alarmControl.StopAnimation("warningAnim"); - _alarmControl.Visible = false; - if (maxState == ParticleAcceleratorPowerState.Level3 && enabled && assembled) + _maxStrength = maxState == ParticleAcceleratorPowerState.Level3 ? 4 : 3; + if (_maxStrength > 3 && enabled && assembled) { _shouldContinueAnimating = true; - _alarmControl.PlayAnimation(_alarmControlAnimation, "warningAnim"); + if (!_alarmControl.Visible) + _alarmControl.PlayAnimation(_alarmControlAnimation, "warningAnim"); } + else + _shouldContinueAnimating = false; } private void UpdateUI(bool assembled, bool blocked, bool enabled, bool powerBlock) @@ -430,12 +433,12 @@ namespace Content.Client.ParticleAccelerator.UI private void UpdatePreview(ParticleAcceleratorUIState updateMessage) { _endCapTexture.SetPowerState(updateMessage, updateMessage.EndCapExists); - _fuelChamberTexture.SetPowerState(updateMessage, updateMessage.FuelChamberExists); _controlBoxTexture.SetPowerState(updateMessage, true); + _fuelChamberTexture.SetPowerState(updateMessage, updateMessage.FuelChamberExists); _powerBoxTexture.SetPowerState(updateMessage, updateMessage.PowerBoxExists); - _emitterCenterTexture.SetPowerState(updateMessage, updateMessage.EmitterCenterExists); - _emitterLeftTexture.SetPowerState(updateMessage, updateMessage.EmitterLeftExists); - _emitterRightTexture.SetPowerState(updateMessage, updateMessage.EmitterRightExists); + _emitterStarboardTexture.SetPowerState(updateMessage, updateMessage.EmitterStarboardExists); + _emitterForeTexture.SetPowerState(updateMessage, updateMessage.EmitterForeExists); + _emitterPortTexture.SetPowerState(updateMessage, updateMessage.EmitterPortExists); } protected override void FrameUpdate(FrameEventArgs args) @@ -453,7 +456,7 @@ namespace Content.Client.ParticleAccelerator.UI var watts = 0; if (_lastDraw != 0) { - var val = _drawNoiseGenerator.GetNoise(_time); + var val = _drawNoiseGenerator.GetNoise(_time, 0f); watts = (int) (_lastDraw + val * 5); } @@ -476,7 +479,7 @@ namespace Content.Client.ParticleAccelerator.UI _baseState = name; _rsi = cache.GetResource($"/Textures/Structures/Power/Generation/PA/{name}.rsi").RSI; - AddChild(_base = new TextureRect {Texture = _rsi[$"completed"].Frame0}); + AddChild(_base = new TextureRect { Texture = _rsi[$"completed"].Frame0 }); AddChild(_unlit = new TextureRect()); MinSize = _rsi.Size; } diff --git a/Content.IntegrationTests/Tests/MachineBoardTest.cs b/Content.IntegrationTests/Tests/MachineBoardTest.cs index e1c1718cfb..5ffe0cf91c 100644 --- a/Content.IntegrationTests/Tests/MachineBoardTest.cs +++ b/Content.IntegrationTests/Tests/MachineBoardTest.cs @@ -20,9 +20,9 @@ public sealed class MachineBoardTest "MachineParticleAcceleratorFuelChamberCircuitboard", "MachineParticleAcceleratorFuelChamberCircuitboard", "MachineParticleAcceleratorPowerBoxCircuitboard", - "MachineParticleAcceleratorEmitterLeftCircuitboard", - "MachineParticleAcceleratorEmitterCenterCircuitboard", - "MachineParticleAcceleratorEmitterRightCircuitboard", + "MachineParticleAcceleratorEmitterStarboardCircuitboard", + "MachineParticleAcceleratorEmitterForeCircuitboard", + "MachineParticleAcceleratorEmitterPortCircuitboard", "ParticleAcceleratorComputerCircuitboard" }; @@ -33,7 +33,7 @@ public sealed class MachineBoardTest [Test] public async Task TestMachineBoardHasValidMachine() { - await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true}); + await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true }); var server = pairTracker.Pair.Server; var protoMan = server.ResolveDependency(); @@ -46,13 +46,16 @@ public sealed class MachineBoardTest continue; var mId = mbc.Prototype; - Assert.That(mId, Is.Not.Null, $"Machine board {p.ID} does not have a corresponding machine."); - Assert.That(protoMan.TryIndex(mId, out var mProto), - $"Machine board {p.ID}'s corresponding machine has an invalid prototype."); - Assert.That(mProto.TryGetComponent(out var mComp), - $"Machine board {p.ID}'s corresponding machine {mId} does not have MachineComponent"); - Assert.That(mComp.BoardPrototype, Is.EqualTo(p.ID), - $"Machine {mId}'s BoardPrototype is not equal to it's corresponding machine board, {p.ID}"); + Assert.Multiple(() => + { + Assert.That(mId, Is.Not.Null, $"Machine board {p.ID} does not have a corresponding machine."); + Assert.That(protoMan.TryIndex(mId, out var mProto), + $"Machine board {p.ID}'s corresponding machine has an invalid prototype."); + Assert.That(mProto.TryGetComponent(out var mComp), + $"Machine board {p.ID}'s corresponding machine {mId} does not have MachineComponent"); + Assert.That(mComp.BoardPrototype, Is.EqualTo(p.ID), + $"Machine {mId}'s BoardPrototype is not equal to it's corresponding machine board, {p.ID}"); + }); } }); @@ -66,7 +69,7 @@ public sealed class MachineBoardTest [Test] public async Task TestComputerBoardHasValidComputer() { - await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true}); + await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true }); var server = pairTracker.Pair.Server; var protoMan = server.ResolveDependency(); @@ -79,13 +82,16 @@ public sealed class MachineBoardTest continue; var cId = cbc.Prototype; - Assert.That(cId, Is.Not.Null, $"Computer board \"{p.ID}\" does not have a corresponding computer."); - Assert.That(protoMan.TryIndex(cId, out var cProto), - $"Computer board \"{p.ID}\"'s corresponding computer has an invalid prototype."); - Assert.That(cProto.TryGetComponent(out var cComp), - $"Computer board {p.ID}'s corresponding computer \"{cId}\" does not have ComputerComponent"); - Assert.That(cComp.BoardPrototype, Is.EqualTo(p.ID), - $"Computer \"{cId}\"'s BoardPrototype is not equal to it's corresponding computer board, \"{p.ID}\""); + Assert.Multiple(() => + { + Assert.That(cId, Is.Not.Null, $"Computer board \"{p.ID}\" does not have a corresponding computer."); + Assert.That(protoMan.TryIndex(cId, out var cProto), + $"Computer board \"{p.ID}\"'s corresponding computer has an invalid prototype."); + Assert.That(cProto.TryGetComponent(out var cComp), + $"Computer board {p.ID}'s corresponding computer \"{cId}\" does not have ComputerComponent"); + Assert.That(cComp.BoardPrototype, Is.EqualTo(p.ID), + $"Computer \"{cId}\"'s BoardPrototype is not equal to it's corresponding computer board, \"{p.ID}\""); + }); } }); diff --git a/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorControlBoxComponent.cs b/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorControlBoxComponent.cs index 09e19122d5..a48d7576e2 100644 --- a/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorControlBoxComponent.cs +++ b/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorControlBoxComponent.cs @@ -1,715 +1,170 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using Content.Server.Administration.Logs; -using Content.Server.Mind.Components; -using Content.Server.Power.Components; -using Content.Server.Power.EntitySystems; -using Content.Server.UserInterface; -using Content.Shared.Database; +using Content.Server.ParticleAccelerator.Wires; using Content.Shared.Singularity.Components; -using Robust.Server.GameObjects; -using Robust.Server.Player; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Utility; -using Timer = Robust.Shared.Timing.Timer; -// using Content.Server.WireHacking; -// using static Content.Shared.Wires.SharedWiresComponent; -namespace Content.Server.ParticleAccelerator.Components +namespace Content.Server.ParticleAccelerator.Components; + +// This component is in control of the PA's logic because it's the one to contain the wires for hacking. +// And also it's the only PA component that meaningfully needs to work on its own. +/// +/// Is the computer thing people interact with to control the PA. +/// Also contains primary logic for actual PA behavior, part scanning, etc... +/// +[RegisterComponent] +public sealed class ParticleAcceleratorControlBoxComponent : Component { - // This component is in control of the PA's logic because it's the one to contain the wires for hacking. - // And also it's the only PA component that meaningfully needs to work on its own. /// - /// Is the computer thing people interact with to control the PA. - /// Also contains primary logic for actual PA behavior, part scanning, etc... + /// Whether the PA parts have been correctly arranged to make a functional device. /// - [RegisterComponent] - public sealed class ParticleAcceleratorControlBoxComponent : ParticleAcceleratorPartComponent - { - [Dependency] private readonly IEntityManager _entMan = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IAdminLogManager _adminLogger = default!; - - [ViewVariables] - private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ParticleAcceleratorControlBoxUiKey.Key); - - /// - /// Power receiver for the control console itself. - /// - [ViewVariables] private ApcPowerReceiverComponent _apcPowerReceiverComponent = default!; - - [ViewVariables] private ParticleAcceleratorFuelChamberComponent? _partFuelChamber; - [ViewVariables] private ParticleAcceleratorEndCapComponent? _partEndCap; - [ViewVariables] private ParticleAcceleratorPowerBoxComponent? _partPowerBox; - [ViewVariables] private ParticleAcceleratorEmitterComponent? _partEmitterLeft; - [ViewVariables] private ParticleAcceleratorEmitterComponent? _partEmitterCenter; - [ViewVariables] private ParticleAcceleratorEmitterComponent? _partEmitterRight; - [ViewVariables] private ParticleAcceleratorPowerState _selectedStrength = ParticleAcceleratorPowerState.Standby; - - [ViewVariables] private bool _isAssembled; - - // Enabled: power switch is on - [ViewVariables] private bool _isEnabled; - - // Powered: power switch is on AND the PA is actively firing (if not on standby) - [ViewVariables] private bool _isPowered; - [ViewVariables] private bool _wireInterfaceBlocked; - [ViewVariables] private bool _wirePowerBlocked; - [ViewVariables] private bool _wireLimiterCut; - [ViewVariables] private bool _wireStrengthCut; - [ViewVariables] private CancellationTokenSource? _fireCancelTokenSrc; - - /// - /// Delay between consecutive PA shots. - /// - // Fun fact: - // On /vg/station (can't check TG because lol they removed singulo), - // the PA emitter parts have var/fire_delay = 50. - // For anybody from the future not BYOND-initiated, that's 5 seconds. - // However, /obj/machinery/particle_accelerator/control_box/process(), - // which calls emit_particle() on the emitters, - // only gets called every *2* seconds, because of CarnMC timing. - // So the *actual* effective firing delay of the PA is 6 seconds, not 5 as listed in the code. - // So... - // I have reflected that here to be authentic. - [ViewVariables(VVAccess.ReadWrite)] [DataField("fireDelay")] private TimeSpan _firingDelay = TimeSpan.FromSeconds(6); - - [ViewVariables(VVAccess.ReadWrite)] [DataField("powerDrawBase")] private int _powerDrawBase = 500; - [ViewVariables(VVAccess.ReadWrite)] [DataField("powerDrawMult")] private int _powerDrawMult = 1500; - - [ViewVariables] private bool ConsolePowered => _apcPowerReceiverComponent?.Powered ?? true; - - public ParticleAcceleratorControlBoxComponent() - { - Master = this; - } - - private ParticleAcceleratorPowerState MaxPower => _wireLimiterCut - ? ParticleAcceleratorPowerState.Level3 - : ParticleAcceleratorPowerState.Level2; - - protected override void Initialize() - { - base.Initialize(); - if (UserInterface != null) - { - UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage; - } - - Owner.EnsureComponent(out _apcPowerReceiverComponent); - - _apcPowerReceiverComponent.Load = 250; - } - - // This is the power state for the PA control box itself. - // Keep in mind that the PA itself can keep firing as long as the HV cable under the power box has... power. - public void OnPowerStateChanged(PowerChangedEvent e) - { - UpdateAppearance(); - - if (!e.Powered) - { - UserInterface?.CloseAll(); - } - } - - private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage obj) - { - if (!ConsolePowered) - { - return; - } - - if (_wireInterfaceBlocked) - { - return; - } - - switch (obj.Message) - { - case ParticleAcceleratorSetEnableMessage enableMessage: - if (enableMessage.Enabled) - { - SwitchOn(obj.Session); - } - else - { - SwitchOff(obj.Session); - } - - break; - - case ParticleAcceleratorSetPowerStateMessage stateMessage: - SetStrength(stateMessage.State, obj.Session); - break; - - case ParticleAcceleratorRescanPartsMessage _: - RescanParts(obj.Session); - break; - } - - UpdateUI(); - } - - public void UpdateUI() - { - var draw = 0f; - var receive = 0f; - - if (_isEnabled) - { - draw = _partPowerBox!.PowerConsumerComponent!.DrawRate; - receive = _partPowerBox!.PowerConsumerComponent!.ReceivedPower; - } - - var state = new ParticleAcceleratorUIState( - _isAssembled, - _isEnabled, - _selectedStrength, - (int) draw, - (int) receive, - _partEmitterLeft != null, - _partEmitterCenter != null, - _partEmitterRight != null, - _partPowerBox != null, - _partFuelChamber != null, - _partEndCap != null, - _wireInterfaceBlocked, - MaxPower, - _wirePowerBlocked); - - UserInterface?.SetState(state); - } - - protected override void OnRemove() - { - _fireCancelTokenSrc?.Cancel(); - _fireCancelTokenSrc = null; - - Master = null; - foreach (var part in AllParts()) - { - if (_entMan.TryGetComponent(part.Owner, out ParticleAcceleratorPartComponent? paPart)) - paPart.Master = null; - } - - base.OnRemove(); - } - - /* - void IWires.RegisterWires(WiresComponent.WiresBuilder builder) - { - builder.CreateWire(ParticleAcceleratorControlBoxWires.Toggle); - builder.CreateWire(ParticleAcceleratorControlBoxWires.Strength); - builder.CreateWire(ParticleAcceleratorControlBoxWires.Interface); - builder.CreateWire(ParticleAcceleratorControlBoxWires.Limiter); - builder.CreateWire(ParticleAcceleratorControlBoxWires.Nothing); - } - - public void WiresUpdate(WiresUpdateEventArgs args) - { - switch (args.Identifier) - { - case ParticleAcceleratorControlBoxWires.Toggle: - if (args.Action == WiresAction.Pulse) - { - if (_isEnabled) - { - SwitchOff(); - } - else - { - SwitchOn(); - } - } - else - { - _wirePowerBlocked = args.Action == WiresAction.Cut; - if (_isEnabled) - { - SwitchOff(); - } - } - - break; - - case ParticleAcceleratorControlBoxWires.Strength: - if (args.Action == WiresAction.Pulse) - { - SetStrength(_selectedStrength + 1); - } - else - { - _wireStrengthCut = args.Action == WiresAction.Cut; - } - - break; - - case ParticleAcceleratorControlBoxWires.Interface: - if (args.Action == WiresAction.Pulse) - { - _wireInterfaceBlocked ^= true; - } - else - { - _wireInterfaceBlocked = args.Action == WiresAction.Cut; - } - - break; - - case ParticleAcceleratorControlBoxWires.Limiter: - if (args.Action == WiresAction.Pulse) - { - Owner.PopupMessageEveryone(Loc.GetString("particle-accelerator-control-box-component-wires-update-limiter-on-pulse")); - } - else - { - _wireLimiterCut = args.Action == WiresAction.Cut; - if (_selectedStrength == ParticleAcceleratorPowerState.Level3 && !_wireLimiterCut) - { - // Yes, it's a feature that mending this wire WON'T WORK if the strength wire is also cut. - // Since that blocks SetStrength(). - SetStrength(ParticleAcceleratorPowerState.Level2); - } - } - - break; - } - - UpdateUI(); - UpdateWireStatus(); - } - - private void UpdateWireStatus() - { - if (!_entMan.TryGetComponent(Owner, out WiresComponent? wires)) - { - return; - } - - var powerBlock = _wirePowerBlocked; - var keyboardLight = new StatusLightData(Color.LimeGreen, - _wireInterfaceBlocked - ? StatusLightState.BlinkingFast - : StatusLightState.On, - "KEYB"); - - var powerLight = new StatusLightData( - Color.Yellow, - powerBlock ? StatusLightState.Off : StatusLightState.On, - "POWR"); - - var limiterLight = new StatusLightData( - _wireLimiterCut ? Color.Purple : Color.Teal, - StatusLightState.On, - "LIMT"); - - var strengthLight = new StatusLightData( - Color.Blue, - _wireStrengthCut ? StatusLightState.BlinkingSlow : StatusLightState.On, - "STRC"); - - wires.SetStatus(ParticleAcceleratorWireStatus.Keyboard, keyboardLight); - wires.SetStatus(ParticleAcceleratorWireStatus.Power, powerLight); - wires.SetStatus(ParticleAcceleratorWireStatus.Limiter, limiterLight); - wires.SetStatus(ParticleAcceleratorWireStatus.Strength, strengthLight); - } - */ - - public void RescanParts(IPlayerSession? playerSession = null) - { - SwitchOff(playerSession, true); - foreach (var part in AllParts()) - { - if (_entMan.TryGetComponent(part.Owner, out ParticleAcceleratorPartComponent? paPart)) - paPart.Master = null; - } - - _isAssembled = false; - _partFuelChamber = null; - _partEndCap = null; - _partPowerBox = null; - _partEmitterLeft = null; - _partEmitterCenter = null; - _partEmitterRight = null; - - var xform = _entMan.GetComponent(Owner); - - // Find fuel chamber first by scanning cardinals. - if (xform.Anchored && _entMan.TryGetComponent(xform.GridUid, out MapGridComponent? grid)) - { - foreach (var maybeFuel in grid.GetCardinalNeighborCells(xform.Coordinates)) - { - if (_entMan.TryGetComponent(maybeFuel, out _partFuelChamber)) - { - break; - } - } - } - - if (_partFuelChamber == null) - { - UpdateUI(); - return; - } - - // Align ourselves to match fuel chamber orientation. - // This means that if you mess up the orientation of the control box it's not a big deal, - // because the sprite is far from obvious about the orientation. - xform.LocalRotation = _entMan.GetComponent(_partFuelChamber.Owner).LocalRotation; - - var offsetEndCap = RotateOffset((1, 1)); - var offsetPowerBox = RotateOffset((1, -1)); - var offsetEmitterLeft = RotateOffset((0, -2)); - var offsetEmitterCenter = RotateOffset((1, -2)); - var offsetEmitterRight = RotateOffset((2, -2)); - - ScanPart(offsetEndCap, out _partEndCap); - ScanPart(offsetPowerBox, out _partPowerBox); - - if (!ScanPart(offsetEmitterCenter, out _partEmitterCenter) || - _partEmitterCenter.Type != ParticleAcceleratorEmitterType.Center) - { - // if it's the wrong type we need to clear this to avoid shenanigans. - _partEmitterCenter = null; - } - - if (ScanPart(offsetEmitterLeft, out _partEmitterLeft) && - _partEmitterLeft.Type != ParticleAcceleratorEmitterType.Left) - { - _partEmitterLeft = null; - } - - if (ScanPart(offsetEmitterRight, out _partEmitterRight) && - _partEmitterRight.Type != ParticleAcceleratorEmitterType.Right) - { - _partEmitterRight = null; - } - - _isAssembled = _partFuelChamber != null && - _partPowerBox != null && - _partEmitterCenter != null && - _partEmitterLeft != null && - _partEmitterRight != null && - _partEndCap != null; - - foreach (var part in AllParts()) - { - if (_entMan.TryGetComponent(part.Owner, out ParticleAcceleratorPartComponent? paPart)) - paPart.Master = this; - } - - UpdateUI(); - - Vector2i RotateOffset(in Vector2i vec) - { - var rot = new Angle(_entMan.GetComponent(Owner).LocalRotation); - return (Vector2i) rot.RotateVec(vec); - } - } - - private bool ScanPart(Vector2i offset, [NotNullWhen(true)] out T? part) - where T : Component - { - var xform = _entMan.GetComponent(Owner); - if (!_mapManager.TryGetGrid(xform.GridUid, out var grid)) - { - part = default; - return false; - } - - var coords = xform.Coordinates; - foreach (var ent in grid.GetOffset(coords, offset)) - { - if (_entMan.TryGetComponent(ent, out part) && !part.Deleted) - { - return true; - } - } - - part = default; - return false; - } - - private IEnumerable AllParts() - { - if (_partFuelChamber != null) - yield return _partFuelChamber; - if (_partEndCap != null) - yield return _partEndCap; - if (_partPowerBox != null) - yield return _partPowerBox; - if (_partEmitterLeft != null) - yield return _partEmitterLeft; - if (_partEmitterCenter != null) - yield return _partEmitterCenter; - if (_partEmitterRight != null) - yield return _partEmitterRight; - } - - public void SwitchOn(IPlayerSession? playerSession = null) - { - DebugTools.Assert(_isAssembled); - - if (_isEnabled) - { - return; - } - - // Logging - _entMan.TryGetComponent(playerSession?.AttachedEntity, out MindComponent? mindComponent); - if(mindComponent != null) - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{_entMan.ToPrettyString(mindComponent.Owner):player} has set {_entMan.ToPrettyString(Owner)} to on"); - - _isEnabled = true; - UpdatePowerDraw(); - // If we don't have power yet we'll turn on when we receive more power from the powernet. - // if we do we'll just go and turn on right now. - if (_partPowerBox!.PowerConsumerComponent!.ReceivedPower >= _partPowerBox.PowerConsumerComponent.DrawRate) - { - PowerOn(); - } - - UpdateUI(); - } - - private void UpdatePowerDraw() - { - _partPowerBox!.PowerConsumerComponent!.DrawRate = PowerDrawFor(_selectedStrength); - } - - public void SwitchOff(IPlayerSession? playerSession = null, bool rescan = false) - { - // Logging - _entMan.TryGetComponent(playerSession?.AttachedEntity, out MindComponent? mindComponent); - if(mindComponent != null) - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{_entMan.ToPrettyString(mindComponent.Owner):player} has set {_entMan.ToPrettyString(Owner)} to off{(rescan ? " via rescan" : "")}"); - - _isEnabled = false; - PowerOff(); - UpdateUI(); - } - - private void PowerOn() - { - DebugTools.Assert(_isEnabled); - DebugTools.Assert(_isAssembled); - - if (_isPowered) - { - return; - } - - _isPowered = true; - UpdateFiring(); - UpdatePartVisualStates(); - UpdateUI(); - } - - private void PowerOff() - { - if (!_isPowered) - { - return; - } - - _isPowered = false; - UpdateFiring(); - UpdateUI(); - UpdatePartVisualStates(); - } - - public void SetStrength(ParticleAcceleratorPowerState state, IPlayerSession? playerSession = null) - { - if (_wireStrengthCut) - { - return; - } - - state = (ParticleAcceleratorPowerState) MathHelper.Clamp( - (int) state, - (int) ParticleAcceleratorPowerState.Standby, - (int) MaxPower); - - _selectedStrength = state; - UpdateAppearance(); - UpdatePartVisualStates(); - - // Logging - _entMan.TryGetComponent(playerSession?.AttachedEntity, out MindComponent? mindComponent); - LogImpact impact; - switch (state) - { - default: - case ParticleAcceleratorPowerState.Standby: - case ParticleAcceleratorPowerState.Level0: - impact = LogImpact.Low; - break; - case ParticleAcceleratorPowerState.Level1: - impact = LogImpact.High; - break; - case ParticleAcceleratorPowerState.Level2: - case ParticleAcceleratorPowerState.Level3: - impact = LogImpact.Extreme; - break; - } - if(mindComponent != null) - _adminLogger.Add(LogType.Action, impact, $"{_entMan.ToPrettyString(mindComponent.Owner):player} has set the strength of {_entMan.ToPrettyString(Owner)} to {state}"); - - if (_isEnabled) - { - UpdatePowerDraw(); - UpdateFiring(); - } - } - - private void UpdateAppearance() - { - if (_entMan.TryGetComponent(Owner, out AppearanceComponent? appearance)) - { - appearance.SetData(ParticleAcceleratorVisuals.VisualState, - _apcPowerReceiverComponent!.Powered - ? (ParticleAcceleratorVisualState) _selectedStrength - : ParticleAcceleratorVisualState.Unpowered); - } - } - - private void UpdateFiring() - { - if (!_isPowered || _selectedStrength == ParticleAcceleratorPowerState.Standby) - { - StopFiring(); - } - else - { - StartFiring(); - } - } - - private void StartFiring() - { - EverythingIsWellToFire(); - - _fireCancelTokenSrc?.Cancel(); - _fireCancelTokenSrc = new CancellationTokenSource(); - var cancelToken = _fireCancelTokenSrc.Token; - Timer.SpawnRepeating(_firingDelay, Fire, cancelToken); - } - - private void Fire() - { - EverythingIsWellToFire(); - - _partEmitterCenter!.Fire(_selectedStrength); - _partEmitterLeft!.Fire(_selectedStrength); - _partEmitterRight!.Fire(_selectedStrength); - } - - [Conditional("DEBUG")] - private void EverythingIsWellToFire() - { - DebugTools.Assert(!Deleted); - DebugTools.Assert(_isPowered); - DebugTools.Assert(_selectedStrength != ParticleAcceleratorPowerState.Standby); - DebugTools.Assert(_isAssembled); - DebugTools.Assert(_partEmitterCenter != null); - DebugTools.Assert(_partEmitterLeft != null); - DebugTools.Assert(_partEmitterRight != null); - } - - private void StopFiring() - { - _fireCancelTokenSrc?.Cancel(); - _fireCancelTokenSrc = null; - } - - private int PowerDrawFor(ParticleAcceleratorPowerState strength) - { - return strength switch - { - ParticleAcceleratorPowerState.Standby => 0, - ParticleAcceleratorPowerState.Level0 => 1, - ParticleAcceleratorPowerState.Level1 => 3, - ParticleAcceleratorPowerState.Level2 => 4, - ParticleAcceleratorPowerState.Level3 => 5, - _ => 0 - } * _powerDrawMult + _powerDrawBase; - } - - public void PowerBoxReceivedChanged(PowerConsumerReceivedChanged eventArgs) - { - DebugTools.Assert(_isAssembled); - - if (!_isEnabled) - { - return; - } - - var isPowered = eventArgs.ReceivedPower >= eventArgs.DrawRate; - if (isPowered) - { - PowerOn(); - } - else - { - PowerOff(); - } - - UpdateUI(); - } - - private void UpdatePartVisualStates() - { - // UpdatePartVisualState(ControlBox); - UpdatePartVisualState(_partFuelChamber); - UpdatePartVisualState(_partPowerBox); - UpdatePartVisualState(_partEmitterCenter); - UpdatePartVisualState(_partEmitterLeft); - UpdatePartVisualState(_partEmitterRight); - //no endcap because it has no powerlevel-sprites - } - - private void UpdatePartVisualState(Component? component) - { - if (component == null || !_entMan.TryGetComponent(component.Owner, out var appearanceComponent)) - { - return; - } - - var state = _isPowered - ? (ParticleAcceleratorVisualState) _selectedStrength - : ParticleAcceleratorVisualState.Unpowered; - appearanceComponent.SetData(ParticleAcceleratorVisuals.VisualState, state); - } - - public enum ParticleAcceleratorControlBoxWires - { - /// - /// Pulse toggles Power. Cut permanently turns off until Mend. - /// - Toggle, - - /// - /// Pulsing increases level until at limit. - /// - Strength, - - /// - /// Pulsing toggles Button-Disabled on UI. Cut disables, Mend enables. - /// - Interface, - - /// - /// Pulsing will produce short message about whirring noise. Cutting increases the max level to 3. Mending reduces it back to 2. - /// - Limiter, - - /// - /// Does Nothing - /// - Nothing - } - } + [ViewVariables] + public bool Assembled = false; + + /// + /// Whether the PA is currently set to fire at the console. + /// Requires to be true. + /// + [ViewVariables] + public bool Enabled = false; + + /// + /// Whether the PA actually has the power necessary to fire. + /// Requires to be true. + /// + [ViewVariables] + public bool Powered = false; + + /// + /// Whether the PA is currently firing or charging to fire. + /// Requires to be true. + /// + [ViewVariables] + public bool Firing = false; + + /// + /// Whether the PA is currently firing or charging to fire. + /// Bounded by and . + /// Modified by . + /// + [ViewVariables(VVAccess.ReadWrite)] + public ParticleAcceleratorPowerState SelectedStrength = ParticleAcceleratorPowerState.Standby; + + /// + /// The maximum strength level this particle accelerator can be set to operate at. + /// Modified by . + /// + [ViewVariables] + public ParticleAcceleratorPowerState MaxStrength = ParticleAcceleratorPowerState.Level2; + + /// + /// The power supply unit of the assembled particle accelerator. + /// Implies the existance of a attached to this entity. + /// + [ViewVariables] + public EntityUid? PowerBox; + + /// + /// Whether the PA is currently firing or charging to fire. + /// Implies the existance of a attached to this entity. + /// + [ViewVariables] + public EntityUid? EndCap; + + /// + /// Whether the PA is currently firing or charging to fire. + /// Implies the existance of a attached to this entity. + /// + [ViewVariables] + public EntityUid? FuelChamber; + + /// + /// Whether the PA is currently firing or charging to fire. + /// Implies the existance of a attached to this entity. + /// + [ViewVariables] + public EntityUid? PortEmitter; + + /// + /// Whether the PA is currently firing or charging to fire. + /// Implies the existance of a attached to this entity. + /// + [ViewVariables] + public EntityUid? ForeEmitter; + + /// + /// Whether the PA is currently firing or charging to fire. + /// Implies the existance of a attached to this entity. + /// + [ViewVariables] + public EntityUid? StarboardEmitter; + + /// + /// The amount of power the particle accelerator must be provided with relative to the expected power draw to function. + /// + [ViewVariables] + public const float RequiredPowerRatio = 0.999f; + + /// + /// The amount of power (in watts) the PA draws just by existing as a functional machine. + /// + [DataField("powerDrawBase")] + [ViewVariables(VVAccess.ReadWrite)] + public int BasePowerDraw = 500; + + /// + /// The amount of power (in watts) the PA draws per level when turned on. + /// + [DataField("powerDrawMult")] + [ViewVariables(VVAccess.ReadWrite)] + public int LevelPowerDraw = 1500; + + /// + /// The time at which the PA last fired a wave of particles. + /// + [DataField("lastFire")] + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan LastFire; + + /// + /// The time at which the PA will next fire a wave of particles. + /// + [DataField("nextFire")] + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan NextFire; + + /// + /// Delay between consecutive PA shots. + /// + // Fun fact: + // On /vg/station (can't check TG because lol they removed singulo), + // the PA emitter parts have var/fire_delay = 50. + // For anybody from the future not BYOND-initiated, that's 5 seconds. + // However, /obj/machinery/particle_accelerator/control_box/process(), + // which calls emit_particle() on the emitters, + // only gets called every *2* seconds, because of CarnMC timing. + // So the *actual* effective firing delay of the PA is 6 seconds, not 5 as listed in the code. + // So... + // I have reflected that here to be authentic. + [DataField("chargeTime")] + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan ChargeTime = TimeSpan.FromSeconds(6.0); + + /// + /// Whether the interface has been disabled via a cut wire or not. + /// Modified by . + /// + [ViewVariables] + public bool InterfaceDisabled = false; + + /// + /// Whether the ability to change the strength of the PA has been disabled via a cut wire or not. + /// Modified by . + /// + [ViewVariables] + public bool StrengthLocked = false; + + /// + /// Whether the PA can be turned on. + /// Modified by . + /// + [ViewVariables] + public bool CanBeEnabled = true; } diff --git a/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorEmitterComponent.cs b/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorEmitterComponent.cs index 8dc3658824..434aa6b59c 100644 --- a/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorEmitterComponent.cs +++ b/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorEmitterComponent.cs @@ -1,36 +1,27 @@ using Content.Shared.Singularity.Components; -namespace Content.Server.ParticleAccelerator.Components +namespace Content.Server.ParticleAccelerator.Components; + +[RegisterComponent] +public sealed class ParticleAcceleratorEmitterComponent : Component { - [RegisterComponent] - public sealed class ParticleAcceleratorEmitterComponent : Component + [DataField("emittedPrototype")] + [ViewVariables(VVAccess.ReadWrite)] + public string EmittedPrototype = "ParticlesProjectile"; + + [DataField("emitterType")] + [ViewVariables(VVAccess.ReadWrite)] + public ParticleAcceleratorEmitterType Type = ParticleAcceleratorEmitterType.Fore; + + public override string ToString() { - [DataField("emitterType")] - public ParticleAcceleratorEmitterType Type = ParticleAcceleratorEmitterType.Center; - - public void Fire(ParticleAcceleratorPowerState strength) - { - var entities = IoCManager.Resolve(); - var projectile = entities.SpawnEntity("ParticlesProjectile", entities.GetComponent(Owner).Coordinates); - - if (!entities.TryGetComponent(projectile, out var particleProjectileComponent)) - { - Logger.Error("ParticleAcceleratorEmitter tried firing particles, but they was spawned without a ParticleProjectileComponent"); - return; - } - particleProjectileComponent.Fire(strength, entities.GetComponent(Owner).WorldRotation, Owner); - } - - public override string ToString() - { - return base.ToString() + $" EmitterType:{Type}"; - } - } - - public enum ParticleAcceleratorEmitterType - { - Left, - Center, - Right + return base.ToString() + $" EmitterType:{Type}"; } } + +public enum ParticleAcceleratorEmitterType +{ + Port, + Fore, + Starboard +} diff --git a/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorPartComponent.cs b/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorPartComponent.cs index 7fdf687026..e539ff30a3 100644 --- a/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorPartComponent.cs +++ b/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorPartComponent.cs @@ -1,39 +1,8 @@ -namespace Content.Server.ParticleAccelerator.Components +namespace Content.Server.ParticleAccelerator.Components; + +[RegisterComponent] +public sealed class ParticleAcceleratorPartComponent : Component { - [RegisterComponent] - [Virtual] - public class ParticleAcceleratorPartComponent : Component - { - [ViewVariables] public ParticleAcceleratorControlBoxComponent? Master; - - protected override void Initialize() - { - base.Initialize(); - // FIXME: this has to be an entity system, full stop. - - IoCManager.Resolve().GetComponent(Owner).Anchored = true; - } - - public void OnAnchorChanged() - { - RescanIfPossible(); - } - - protected override void OnRemove() - { - base.OnRemove(); - - RescanIfPossible(); - } - - private void RescanIfPossible() - { - Master?.RescanParts(); - } - - public void Moved() - { - RescanIfPossible(); - } - } + [ViewVariables] + public EntityUid? Master; } diff --git a/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorPowerBoxComponent.cs b/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorPowerBoxComponent.cs index f9f9e06876..a0cbe06f4b 100644 --- a/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorPowerBoxComponent.cs +++ b/Content.Server/ParticleAccelerator/Components/ParticleAcceleratorPowerBoxComponent.cs @@ -1,16 +1,6 @@ -using Content.Server.Power.Components; - -namespace Content.Server.ParticleAccelerator.Components; +namespace Content.Server.ParticleAccelerator.Components; [RegisterComponent] public sealed class ParticleAcceleratorPowerBoxComponent : Component { - [ViewVariables] public PowerConsumerComponent? PowerConsumerComponent; - - protected override void Initialize() - { - base.Initialize(); - - PowerConsumerComponent = Owner.EnsureComponentWarn(); - } } diff --git a/Content.Server/ParticleAccelerator/Components/ParticleProjectileComponent.cs b/Content.Server/ParticleAccelerator/Components/ParticleProjectileComponent.cs index f87cc507fd..b2438f0700 100644 --- a/Content.Server/ParticleAccelerator/Components/ParticleProjectileComponent.cs +++ b/Content.Server/ParticleAccelerator/Components/ParticleProjectileComponent.cs @@ -1,66 +1,9 @@ -using Content.Server.Projectiles; -using Content.Server.Singularity.Components; -using Content.Shared.Projectiles; using Content.Shared.Singularity.Components; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Timing; -namespace Content.Server.ParticleAccelerator.Components +namespace Content.Server.ParticleAccelerator.Components; + +[RegisterComponent] +public sealed class ParticleProjectileComponent : Component { - [RegisterComponent] - public sealed class ParticleProjectileComponent : Component - { - [Dependency] private readonly IEntityManager _entMan = default!; - - public ParticleAcceleratorPowerState State; - - public void Fire(ParticleAcceleratorPowerState state, Angle angle, EntityUid firer) - { - State = state; - - if (!_entMan.TryGetComponent(Owner, out var physicsComponent)) - { - Logger.Error("ParticleProjectile tried firing, but it was spawned without a CollidableComponent"); - return; - } - - var physics = _entMan.System(); - physics.SetBodyStatus(physicsComponent, BodyStatus.InAir); - - if (!_entMan.TryGetComponent(Owner, out var projectileComponent)) - { - Logger.Error("ParticleProjectile tried firing, but it was spawned without a ProjectileComponent"); - return; - } - - _entMan.EntitySysManager.GetEntitySystem().SetShooter(projectileComponent, firer); - - if (!_entMan.TryGetComponent(Owner, out var singuloFoodComponent)) - { - Logger.Error("ParticleProjectile tried firing, but it was spawned without a SinguloFoodComponent"); - return; - } - var multiplier = State switch - { - ParticleAcceleratorPowerState.Standby => 0, - ParticleAcceleratorPowerState.Level0 => 1, - ParticleAcceleratorPowerState.Level1 => 3, - ParticleAcceleratorPowerState.Level2 => 6, - ParticleAcceleratorPowerState.Level3 => 10, - _ => 0 - }; - singuloFoodComponent.Energy = 10 * multiplier; - - if (_entMan.TryGetComponent(Owner, out AppearanceComponent? appearance)) - { - appearance.SetData(ParticleAcceleratorVisuals.VisualState, state); - } - - physics.SetLinearVelocity(Owner, angle.ToWorldVec() * 20f, body: physicsComponent); - - _entMan.GetComponent(Owner).LocalRotation = angle; - Timer.Spawn(3000, () => _entMan.DeleteEntity(Owner)); - } - } + public ParticleAcceleratorPowerState State; } diff --git a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs index b479b541b0..0af4bbe474 100644 --- a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs +++ b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs @@ -1,5 +1,11 @@ +using Content.Server.Mind.Components; using Content.Server.ParticleAccelerator.Components; using Content.Server.Power.Components; +using Content.Shared.Database; +using Content.Shared.Singularity.Components; +using Robust.Server.Player; +using Robust.Shared.Utility; +using System.Diagnostics; namespace Content.Server.ParticleAccelerator.EntitySystems; @@ -7,11 +13,361 @@ public sealed partial class ParticleAcceleratorSystem { private void InitializeControlBoxSystem() { + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(OnControlBoxPowerChange); + SubscribeLocalEvent(OnUISetEnableMessage); + SubscribeLocalEvent(OnUISetPowerMessage); + SubscribeLocalEvent(OnUIRescanMessage); } - private static void OnControlBoxPowerChange(EntityUid uid, ParticleAcceleratorControlBoxComponent component, ref PowerChangedEvent args) + public override void Update(float frameTime) { - component.OnPowerStateChanged(args); + var curTime = _gameTiming.CurTime; + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var controller)) + { + if (controller.Firing && curTime >= controller.NextFire) + Fire(uid, curTime, controller); + } + } + + [Conditional("DEBUG")] + private void EverythingIsWellToFire(ParticleAcceleratorControlBoxComponent controller) + { + DebugTools.Assert(controller.Powered); + DebugTools.Assert(controller.SelectedStrength != ParticleAcceleratorPowerState.Standby); + DebugTools.Assert(controller.Assembled); + DebugTools.Assert(EntityManager.EntityExists(controller.PortEmitter)); + DebugTools.Assert(EntityManager.EntityExists(controller.ForeEmitter)); + DebugTools.Assert(EntityManager.EntityExists(controller.StarboardEmitter)); + } + + public void Fire(EntityUid uid, TimeSpan curTime, ParticleAcceleratorControlBoxComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + comp.LastFire = curTime; + comp.NextFire = curTime + comp.ChargeTime; + + EverythingIsWellToFire(comp); + + var strength = comp.SelectedStrength; + FireEmitter(comp.PortEmitter!.Value, strength); + FireEmitter(comp.ForeEmitter!.Value, strength); + FireEmitter(comp.StarboardEmitter!.Value, strength); + } + + public void SwitchOn(EntityUid uid, IPlayerSession? user = null, ParticleAcceleratorControlBoxComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + DebugTools.Assert(comp.Assembled); + + if (comp.Enabled || !comp.CanBeEnabled) + return; + + if (HasComp(user?.AttachedEntity)) + _adminLogger.Add(LogType.Action, LogImpact.Low, $"{EntityManager.ToPrettyString((EntityUid) user!.AttachedEntity):player} has turned {EntityManager.ToPrettyString(uid)} on"); + + comp.Enabled = true; + UpdatePowerDraw(uid, comp); + + if (!TryComp(comp.PowerBox, out var powerConsumer) + || powerConsumer.ReceivedPower >= powerConsumer.DrawRate * ParticleAcceleratorControlBoxComponent.RequiredPowerRatio) + PowerOn(uid, comp); + + UpdateUI(uid, comp); + } + + public void SwitchOff(EntityUid uid, IPlayerSession? user = null, ParticleAcceleratorControlBoxComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + if (!comp.Enabled) + return; + + if (HasComp(user?.AttachedEntity)) + _adminLogger.Add(LogType.Action, LogImpact.Low, $"{EntityManager.ToPrettyString((EntityUid) user!.AttachedEntity):player} has turned {EntityManager.ToPrettyString(uid)} off"); + + comp.Enabled = false; + UpdatePowerDraw(uid, comp); + PowerOff(uid, comp); + UpdateUI(uid, comp); + } + + public void PowerOn(EntityUid uid, ParticleAcceleratorControlBoxComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + DebugTools.Assert(comp.Enabled); + DebugTools.Assert(comp.Assembled); + + if (comp.Powered) + return; + + comp.Powered = true; + UpdatePowerDraw(uid, comp); + UpdateFiring(uid, comp); + UpdatePartVisualStates(uid, comp); + UpdateUI(uid, comp); + } + + public void PowerOff(EntityUid uid, ParticleAcceleratorControlBoxComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + if (!comp.Powered) + return; + + comp.Powered = false; + UpdatePowerDraw(uid, comp); + UpdateFiring(uid, comp); + UpdatePartVisualStates(uid, comp); + UpdateUI(uid, comp); + } + + public void SetStrength(EntityUid uid, ParticleAcceleratorPowerState strength, IPlayerSession? user = null, ParticleAcceleratorControlBoxComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + if (comp.StrengthLocked) + return; + + strength = (ParticleAcceleratorPowerState) MathHelper.Clamp( + (int) strength, + (int) ParticleAcceleratorPowerState.Standby, + (int) comp.MaxStrength + ); + + if (strength == comp.SelectedStrength) + return; + + if (HasComp(user?.AttachedEntity)) + { + var impact = strength switch + { + ParticleAcceleratorPowerState.Standby => LogImpact.Low, + ParticleAcceleratorPowerState.Level0 => LogImpact.Medium, + ParticleAcceleratorPowerState.Level1 => LogImpact.High, + ParticleAcceleratorPowerState.Level2 + or ParticleAcceleratorPowerState.Level3 + or _ => LogImpact.Extreme, + }; + + _adminLogger.Add(LogType.Action, impact, $"{EntityManager.ToPrettyString(user!.AttachedEntity!.Value):player} has set the strength of {EntityManager.ToPrettyString(uid)} to {strength}"); + } + + comp.SelectedStrength = strength; + UpdateAppearance(uid, comp); + UpdatePartVisualStates(uid, comp); + + if (comp.Enabled) + { + UpdatePowerDraw(uid, comp); + UpdateFiring(uid, comp); + } + } + + private void UpdateFiring(EntityUid uid, ParticleAcceleratorControlBoxComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + if (!comp.Powered || comp.SelectedStrength < ParticleAcceleratorPowerState.Level0) + { + comp.Firing = false; + return; + } + + EverythingIsWellToFire(comp); + + var curTime = _gameTiming.CurTime; + comp.LastFire = curTime; + comp.NextFire = curTime + comp.ChargeTime; + comp.Firing = true; + } + + private void UpdatePowerDraw(EntityUid uid, ParticleAcceleratorControlBoxComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + if (!TryComp(comp.PowerBox, out var powerConsumer)) + return; + + var powerDraw = comp.BasePowerDraw; + if (comp.Enabled) + powerDraw += comp.LevelPowerDraw * (int) comp.SelectedStrength; + + powerConsumer.DrawRate = powerDraw; + } + + private void UpdateUI(EntityUid uid, ParticleAcceleratorControlBoxComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + if (!_uiSystem.TryGetUi(uid, ParticleAcceleratorControlBoxUiKey.Key, out var bui)) + return; + + var draw = 0f; + var receive = 0f; + + if (TryComp(comp.PowerBox, out var powerConsumer)) + { + draw = powerConsumer.DrawRate; + receive = powerConsumer.ReceivedPower; + } + + _uiSystem.SetUiState(bui, new ParticleAcceleratorUIState( + comp.Assembled, + comp.Enabled, + comp.SelectedStrength, + (int) draw, + (int) receive, + comp.StarboardEmitter != null, + comp.ForeEmitter != null, + comp.PortEmitter != null, + comp.PowerBox != null, + comp.FuelChamber != null, + comp.EndCap != null, + comp.InterfaceDisabled, + comp.MaxStrength, + comp.StrengthLocked + )); + } + + private void UpdateAppearance(EntityUid uid, ParticleAcceleratorControlBoxComponent? comp = null, AppearanceComponent? appearance = null) + { + if (!Resolve(uid, ref comp)) + return; + + _appearanceSystem.SetData( + uid, + ParticleAcceleratorVisuals.VisualState, + TryComp(uid, out var apcPower) && !apcPower.Powered + ? ParticleAcceleratorVisualState.Unpowered + : (ParticleAcceleratorVisualState) comp.SelectedStrength, + appearance + ); + } + + private void UpdatePartVisualStates(EntityUid uid, ParticleAcceleratorControlBoxComponent? controller = null) + { + if (!Resolve(uid, ref controller)) + return; + + var state = controller.Powered ? (ParticleAcceleratorVisualState) controller.SelectedStrength : ParticleAcceleratorVisualState.Unpowered; + + // UpdatePartVisualState(ControlBox); (We are the control box) + if (controller.FuelChamber.HasValue) + _appearanceSystem.SetData(controller.FuelChamber!.Value, ParticleAcceleratorVisuals.VisualState, state); + if (controller.PowerBox.HasValue) + _appearanceSystem.SetData(controller.PowerBox!.Value, ParticleAcceleratorVisuals.VisualState, state); + if (controller.PortEmitter.HasValue) + _appearanceSystem.SetData(controller.PortEmitter!.Value, ParticleAcceleratorVisuals.VisualState, state); + if (controller.ForeEmitter.HasValue) + _appearanceSystem.SetData(controller.ForeEmitter!.Value, ParticleAcceleratorVisuals.VisualState, state); + if (controller.StarboardEmitter.HasValue) + _appearanceSystem.SetData(controller.StarboardEmitter!.Value, ParticleAcceleratorVisuals.VisualState, state); + //no endcap because it has no powerlevel-sprites + } + + private IEnumerable AllParts(EntityUid uid, ParticleAcceleratorControlBoxComponent? comp = null) + { + if (Resolve(uid, ref comp)) + { + if (comp.FuelChamber.HasValue) + yield return comp.FuelChamber.Value; + if (comp.EndCap.HasValue) + yield return comp.EndCap.Value; + if (comp.PowerBox.HasValue) + yield return comp.PowerBox.Value; + if (comp.PortEmitter.HasValue) + yield return comp.PortEmitter.Value; + if (comp.ForeEmitter.HasValue) + yield return comp.ForeEmitter.Value; + if (comp.StarboardEmitter.HasValue) + yield return comp.StarboardEmitter.Value; + } + } + + private void OnComponentStartup(EntityUid uid, ParticleAcceleratorControlBoxComponent comp, ComponentStartup args) + { + if (TryComp(uid, out var part)) + part.Master = uid; + } + + private void OnComponentShutdown(EntityUid uid, ParticleAcceleratorControlBoxComponent comp, ComponentShutdown args) + { + if (TryComp(uid, out var partStatus)) + partStatus.Master = null; + + var partQuery = GetEntityQuery(); + foreach (var part in AllParts(uid, comp)) + { + if (partQuery.TryGetComponent(part, out var partData)) + partData.Master = null; + } + } + + // This is the power state for the PA control box itself. + // Keep in mind that the PA itself can keep firing as long as the HV cable under the power box has... power. + private void OnControlBoxPowerChange(EntityUid uid, ParticleAcceleratorControlBoxComponent comp, ref PowerChangedEvent args) + { + UpdateAppearance(uid, comp); + + if (!args.Powered) + _uiSystem.TryCloseAll(uid, ParticleAcceleratorControlBoxUiKey.Key); + } + + private void OnUISetEnableMessage(EntityUid uid, ParticleAcceleratorControlBoxComponent comp, ParticleAcceleratorSetEnableMessage msg) + { + if (!ParticleAcceleratorControlBoxUiKey.Key.Equals(msg.UiKey)) + return; + if (comp.InterfaceDisabled) + return; + if (TryComp(uid, out var apcPower) && !apcPower.Powered) + return; + + if (msg.Enabled) + { + if (comp.Assembled) + SwitchOn(uid, (IPlayerSession?) msg.Session, comp); + } + else + SwitchOff(uid, (IPlayerSession?) msg.Session, comp); + + UpdateUI(uid, comp); + } + + private void OnUISetPowerMessage(EntityUid uid, ParticleAcceleratorControlBoxComponent comp, ParticleAcceleratorSetPowerStateMessage msg) + { + if (!ParticleAcceleratorControlBoxUiKey.Key.Equals(msg.UiKey)) + return; + if (comp.InterfaceDisabled) + return; + if (TryComp(uid, out var apcPower) && !apcPower.Powered) + return; + + SetStrength(uid, msg.State, (IPlayerSession?) msg.Session, comp); + + UpdateUI(uid, comp); + } + + private void OnUIRescanMessage(EntityUid uid, ParticleAcceleratorControlBoxComponent comp, ParticleAcceleratorRescanPartsMessage msg) + { + if (!ParticleAcceleratorControlBoxUiKey.Key.Equals(msg.UiKey)) + return; + if (comp.InterfaceDisabled) + return; + if (TryComp(uid, out var apcPower) && !apcPower.Powered) + return; + + RescanParts(uid, (IPlayerSession?) msg.Session, comp); + + UpdateUI(uid, comp); } } diff --git a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.Emitter.cs b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.Emitter.cs new file mode 100644 index 0000000000..1dfcb4241c --- /dev/null +++ b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.Emitter.cs @@ -0,0 +1,62 @@ +using Content.Server.ParticleAccelerator.Components; +using Content.Server.Singularity.Components; +using Content.Shared.Projectiles; +using Content.Shared.Singularity.Components; +using Robust.Shared.Physics.Components; + +namespace Content.Server.ParticleAccelerator.EntitySystems; + +public sealed partial class ParticleAcceleratorSystem +{ + private void FireEmitter(EntityUid uid, ParticleAcceleratorPowerState strength, ParticleAcceleratorEmitterComponent? emitter = null) + { + if (!Resolve(uid, ref emitter)) + return; + + var xformQuery = GetEntityQuery(); + if (!xformQuery.TryGetComponent(uid, out var xform)) + { + Logger.Error("ParticleAccelerator attempted to emit a particle without (having) a transform from which to base its initial position and orientation."); + return; + } + + var emitted = Spawn(emitter.EmittedPrototype, xform.Coordinates); + + if (xformQuery.TryGetComponent(emitted, out var particleXform)) + _transformSystem.SetLocalRotation(emitted, xform.LocalRotation, particleXform); + + if (TryComp(emitted, out var particlePhys)) + { + var angle = _transformSystem.GetWorldRotation(uid, xformQuery); + _physicsSystem.SetBodyStatus(particlePhys, BodyStatus.InAir); + + var velocity = angle.ToWorldVec() * 20f; + if (TryComp(uid, out var phys)) + velocity += phys.LinearVelocity; // Inherit velocity from parent so if the clown has strapped a dozen engines to departures we don't outpace the particles. + + _physicsSystem.SetLinearVelocity(emitted, velocity, body: particlePhys); + } + + if (TryComp(emitted, out var proj)) + _projectileSystem.SetShooter(proj, uid); + + if (TryComp(emitted, out var food)) + { + // TODO: Unhardcode this. + food.Energy = strength switch + { + ParticleAcceleratorPowerState.Standby => 0, + ParticleAcceleratorPowerState.Level0 => 1, + ParticleAcceleratorPowerState.Level1 => 3, + ParticleAcceleratorPowerState.Level2 => 6, + ParticleAcceleratorPowerState.Level3 => 10, + _ => 0, + } * 10; + } + + if (TryComp(emitted, out var particle)) + particle.State = strength; + + _appearanceSystem.SetData(emitted, ParticleAcceleratorVisuals.VisualState, strength); + } +} diff --git a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.Parts.cs b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.Parts.cs index 62fe92c3fc..409b78ad1b 100644 --- a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.Parts.cs +++ b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.Parts.cs @@ -1,30 +1,162 @@ using Content.Server.ParticleAccelerator.Components; using JetBrains.Annotations; -using Robust.Shared.Physics.Components; +using Robust.Server.Player; +using Robust.Shared.Map.Components; using Robust.Shared.Physics.Events; +using System.Diagnostics.CodeAnalysis; -namespace Content.Server.ParticleAccelerator.EntitySystems +namespace Content.Server.ParticleAccelerator.EntitySystems; + +[UsedImplicitly] +public sealed partial class ParticleAcceleratorSystem { - [UsedImplicitly] - public sealed partial class ParticleAcceleratorSystem + private void InitializePartSystem() { - private void InitializePartSystem() + SubscribeLocalEvent(OnComponentShutdown); + SubscribeLocalEvent(OnMoveEvent); + SubscribeLocalEvent(BodyTypeChanged); + } + + public void RescanParts(EntityUid uid, IPlayerSession? user = null, ParticleAcceleratorControlBoxComponent? controller = null) + { + if (!Resolve(uid, ref controller)) + return; + + SwitchOff(uid, user, controller); + + var partQuery = GetEntityQuery(); + foreach (var part in AllParts(uid, controller)) { - SubscribeLocalEvent(OnMoveEvent); - SubscribeLocalEvent(BodyTypeChanged); + if (partQuery.TryGetComponent(part, out var partState)) + partState.Master = null; } - private static void BodyTypeChanged( - EntityUid uid, - ParticleAcceleratorPartComponent component, - ref PhysicsBodyTypeChangedEvent args) + controller.Assembled = false; + controller.FuelChamber = null; + controller.EndCap = null; + controller.PowerBox = null; + controller.PortEmitter = null; + controller.ForeEmitter = null; + controller.StarboardEmitter = null; + + var xformQuery = GetEntityQuery(); + if (!xformQuery.TryGetComponent(uid, out var xform) || !xform.Anchored) + return; + + var gridUid = xform.GridUid; + if (gridUid == null || gridUid != xform.ParentUid || !_mapManager.TryGetGrid(gridUid, out var grid)) + return; + + // Find fuel chamber first by scanning cardinals. + var fuelQuery = GetEntityQuery(); + foreach (var adjacent in grid.GetCardinalNeighborCells(xform.Coordinates)) { - component.OnAnchorChanged(); + if (fuelQuery.HasComponent(adjacent) + && partQuery.TryGetComponent(adjacent, out var partState) + && partState.Master == null) + { + controller.FuelChamber = adjacent; + break; + } } - private static void OnMoveEvent(EntityUid uid, ParticleAcceleratorPartComponent component, ref MoveEvent args) + if (controller.FuelChamber == null) { - component.Moved(); + UpdateUI(uid, controller); + return; } + + // Align ourselves to match fuel chamber orientation. + // This means that if you mess up the orientation of the control box it's not a big deal, + // because the sprite is far from obvious about the orientation. + var fuelXform = xformQuery.GetComponent(controller.FuelChamber!.Value); + var rotation = fuelXform.LocalRotation; + _transformSystem.SetLocalRotation(uid, rotation, xform); + + // Calculate offsets for each of the parts of the PA. + // These are all done relative to the fuel chamber BC that is basically the center of the machine. + var positionFuelChamber = grid.TileIndicesFor(fuelXform.Coordinates); // // + var positionEndCap = positionFuelChamber + (Vector2i) rotation.RotateVec((0, 1)); // n // n: End Cap + var positionPowerBox = positionFuelChamber + (Vector2i) rotation.RotateVec((0, -1)); // CF // C: Control Box, F: Fuel Chamber + var positionPortEmitter = positionFuelChamber + (Vector2i) rotation.RotateVec((1, -2)); // P // P: Power Box + var positionForeEmitter = positionFuelChamber + (Vector2i) rotation.RotateVec((0, -2)); // EEE // E: Emitter (Starboard, Fore, Port) + var positionStarboardEmitter = positionFuelChamber + (Vector2i) rotation.RotateVec((-1, -2)); // // + + ScanPart(gridUid!.Value, positionEndCap, rotation, out controller.EndCap, out var _, grid); + ScanPart(gridUid!.Value, positionPowerBox, rotation, out controller.PowerBox, out var _, grid); + + if (!ScanPart(gridUid!.Value, positionPortEmitter, rotation, out controller.PortEmitter, out var portEmitter, grid) + || portEmitter!.Type != ParticleAcceleratorEmitterType.Port) + controller.PortEmitter = null; + + if (!ScanPart(gridUid!.Value, positionForeEmitter, rotation, out controller.ForeEmitter, out var foreEmitter, grid) + || foreEmitter!.Type != ParticleAcceleratorEmitterType.Fore) + controller.ForeEmitter = null; + + if (!ScanPart(gridUid!.Value, positionStarboardEmitter, rotation, out controller.StarboardEmitter, out var starboardEmitter, grid) + || starboardEmitter!.Type != ParticleAcceleratorEmitterType.Starboard) + controller.StarboardEmitter = null; + + controller.Assembled = + controller.FuelChamber.HasValue + && controller.EndCap.HasValue + && controller.PowerBox.HasValue + && controller.PortEmitter.HasValue + && controller.ForeEmitter.HasValue + && controller.StarboardEmitter.HasValue; + + foreach (var part in AllParts(uid, controller)) + { + if (partQuery.TryGetComponent(part, out var partState)) + partState.Master = uid; + } + + UpdatePowerDraw(uid, controller); + UpdateUI(uid, controller); + } + + private bool ScanPart(EntityUid uid, Vector2i coordinates, Angle? rotation, [NotNullWhen(true)] out EntityUid? part, [NotNullWhen(true)] out T? comp, MapGridComponent? grid = null) + where T : Component + { + if (!Resolve(uid, ref grid)) + { + part = null; + comp = null; + return false; + } + + var compQuery = GetEntityQuery(); + foreach (var entity in grid.GetAnchoredEntities(coordinates)) + { + if (compQuery.TryGetComponent(entity, out comp) + && TryComp(entity, out var partState) && partState.Master == null + && (rotation == null || MathHelper.CloseTo(Transform(entity).LocalRotation.Theta, rotation!.Value.Theta))) + { + part = entity; + return true; + } + } + + part = null; + comp = null; + return false; + } + + private void OnComponentShutdown(EntityUid uid, ParticleAcceleratorPartComponent comp, ComponentShutdown args) + { + if (EntityManager.EntityExists(comp.Master)) + RescanParts(comp.Master!.Value); + } + + private void BodyTypeChanged(EntityUid uid, ParticleAcceleratorPartComponent comp, ref PhysicsBodyTypeChangedEvent args) + { + if (EntityManager.EntityExists(comp.Master)) + RescanParts(comp.Master!.Value); + } + + private void OnMoveEvent(EntityUid uid, ParticleAcceleratorPartComponent comp, ref MoveEvent args) + { + if (EntityManager.EntityExists(comp.Master)) + RescanParts(comp.Master!.Value); } } diff --git a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.PowerBox.cs b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.PowerBox.cs index ff8defbbc5..be961ba5fd 100644 --- a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.PowerBox.cs +++ b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.PowerBox.cs @@ -1,22 +1,28 @@ using Content.Server.ParticleAccelerator.Components; using Content.Server.Power.EntitySystems; -namespace Content.Server.ParticleAccelerator.EntitySystems -{ - public sealed partial class ParticleAcceleratorSystem - { - private void InitializePowerBoxSystem() - { - SubscribeLocalEvent(PowerBoxReceivedChanged); - } +namespace Content.Server.ParticleAccelerator.EntitySystems; - private void PowerBoxReceivedChanged( - EntityUid uid, - ParticleAcceleratorPowerBoxComponent component, - ref PowerConsumerReceivedChanged args) - { - if (TryComp(uid, out ParticleAcceleratorPartComponent? paPart)) - paPart.Master?.PowerBoxReceivedChanged(args); - } +public sealed partial class ParticleAcceleratorSystem +{ + private void InitializePowerBoxSystem() + { + SubscribeLocalEvent(PowerBoxReceivedChanged); + } + + private void PowerBoxReceivedChanged(EntityUid uid, ParticleAcceleratorPowerBoxComponent component, ref PowerConsumerReceivedChanged args) + { + if (!TryComp(uid, out var part)) + return; + if (!TryComp(part.Master, out var controller)) + return; + + var master = part.Master!.Value; + if (controller.Enabled && args.ReceivedPower >= args.DrawRate * ParticleAcceleratorControlBoxComponent.RequiredPowerRatio) + PowerOn(master, comp: controller); + else + PowerOff(master, comp: controller); + + UpdateUI(master, controller); } } diff --git a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs index a3c8a19779..aab89c1b7c 100644 --- a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs +++ b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs @@ -1,7 +1,23 @@ +using Content.Server.Administration.Logs; +using Content.Server.Projectiles; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Map; +using Robust.Shared.Timing; +using Robust.Server.GameObjects; + namespace Content.Server.ParticleAccelerator.EntitySystems; public sealed partial class ParticleAcceleratorSystem : EntitySystem { + [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly ProjectileSystem _projectileSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + public override void Initialize() { base.Initialize(); diff --git a/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorInterfaceWireAction.cs b/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorInterfaceWireAction.cs new file mode 100644 index 0000000000..8c6046a72d --- /dev/null +++ b/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorInterfaceWireAction.cs @@ -0,0 +1,35 @@ +using Content.Server.ParticleAccelerator.Components; +using Content.Server.Wires; +using Content.Shared.Singularity.Components; +using Content.Shared.Wires; + +namespace Content.Server.ParticleAccelerator.Wires; + +public sealed class ParticleAcceleratorKeyboardWireAction : ComponentWireAction +{ + public override string Name { get; set; } = "wire-name-pa-keyboard"; + public override Color Color { get; set; } = Color.LimeGreen; + public override object StatusKey { get; } = ParticleAcceleratorWireStatus.Keyboard; + + public override StatusLightState? GetLightState(Wire wire, ParticleAcceleratorControlBoxComponent component) + { + return component.InterfaceDisabled ? StatusLightState.BlinkingFast : StatusLightState.On; + } + + public override bool Cut(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + controller.InterfaceDisabled = true; + return true; + } + + public override bool Mend(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + controller.InterfaceDisabled = false; + return true; + } + + public override void Pulse(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + controller.InterfaceDisabled = !controller.InterfaceDisabled; + } +} diff --git a/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorLimiterWireAction.cs b/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorLimiterWireAction.cs new file mode 100644 index 0000000000..dabebf7ebb --- /dev/null +++ b/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorLimiterWireAction.cs @@ -0,0 +1,69 @@ +using Content.Server.ParticleAccelerator.Components; +using Content.Server.ParticleAccelerator.EntitySystems; +using Content.Server.Popups; +using Content.Server.Wires; +using Content.Shared.Popups; +using Content.Shared.Singularity.Components; +using Content.Shared.Wires; +using Robust.Server.GameObjects; + +namespace Content.Server.ParticleAccelerator.Wires; + +public sealed class ParticleAcceleratorLimiterWireAction : ComponentWireAction +{ + public override string Name { get; set; } = "wire-name-pa-limiter"; + public override Color Color { get; set; } = Color.Teal; + public override object StatusKey { get; } = ParticleAcceleratorWireStatus.Limiter; + + public override StatusLightData? GetStatusLightData(Wire wire) + { + var result = base.GetStatusLightData(wire); + + if (result.HasValue + && EntityManager.TryGetComponent(wire.Owner, out var controller) + && controller.MaxStrength >= ParticleAcceleratorPowerState.Level3) + result = new(Color.Purple, result.Value.State, result.Value.Text); + + return result; + } + + public override StatusLightState? GetLightState(Wire wire, ParticleAcceleratorControlBoxComponent component) + { + return StatusLightState.On; + } + + public override bool Cut(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + controller.MaxStrength = ParticleAcceleratorPowerState.Level3; + return true; + } + + public override bool Mend(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + + controller.MaxStrength = ParticleAcceleratorPowerState.Level2; + if (controller.SelectedStrength <= controller.MaxStrength || controller.StrengthLocked) + return true; + + // Yes, it's a feature that mending this wire WON'T WORK if the strength wire is also cut. + // Since that blocks SetStrength(). + var paSystem = EntityManager.System(); + var userSession = EntityManager.TryGetComponent(user, out var actor) ? actor.PlayerSession : null; + paSystem.SetStrength(wire.Owner, controller.MaxStrength, userSession, controller); + return true; + } + + public override void Pulse(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + EntityManager.System().PopupEntity( + Loc.GetString("particle-accelerator-control-box-component-wires-update-limiter-on-pulse"), + user, + PopupType.SmallCaution + ); + } + + public override void Update(Wire wire) + { + + } +} diff --git a/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorStrengthWireAction.cs b/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorStrengthWireAction.cs new file mode 100644 index 0000000000..3e5c78d4ad --- /dev/null +++ b/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorStrengthWireAction.cs @@ -0,0 +1,40 @@ +using Content.Server.ParticleAccelerator.Components; +using Content.Server.ParticleAccelerator.EntitySystems; +using Content.Server.Wires; +using Content.Shared.Singularity.Components; +using Content.Shared.Wires; +using Robust.Server.GameObjects; +using Robust.Shared.Random; + +namespace Content.Server.ParticleAccelerator.Wires; + +public sealed class ParticleAcceleratorStrengthWireAction : ComponentWireAction +{ + public override string Name { get; set; } = "wire-name-pa-strength"; + public override Color Color { get; set; } = Color.Blue; + public override object StatusKey { get; } = ParticleAcceleratorWireStatus.Strength; + + public override StatusLightState? GetLightState(Wire wire, ParticleAcceleratorControlBoxComponent component) + { + return component.StrengthLocked ? StatusLightState.BlinkingSlow : StatusLightState.On; + } + + public override bool Cut(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + controller.StrengthLocked = true; + return true; + } + + public override bool Mend(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + controller.StrengthLocked = false; + return true; + } + + public override void Pulse(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + var paSystem = EntityManager.System(); + var userSession = EntityManager.TryGetComponent(user, out var actor) ? actor.PlayerSession : null; + paSystem.SetStrength(wire.Owner, (ParticleAcceleratorPowerState) ((int) controller.SelectedStrength + 1), userSession, controller); + } +} diff --git a/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorToggleWireAction.cs b/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorToggleWireAction.cs new file mode 100644 index 0000000000..22dbf95196 --- /dev/null +++ b/Content.Server/ParticleAccelerator/Wires/ParticleAcceleratorToggleWireAction.cs @@ -0,0 +1,49 @@ +using Content.Server.ParticleAccelerator.Components; +using Content.Server.ParticleAccelerator.EntitySystems; +using Content.Server.Wires; +using Content.Shared.Singularity.Components; +using Content.Shared.Wires; +using Robust.Server.GameObjects; + +namespace Content.Server.ParticleAccelerator.Wires; + +public sealed class ParticleAcceleratorPowerWireAction : ComponentWireAction +{ + public override string Name { get; set; } = "wire-name-pa-power"; + public override Color Color { get; set; } = Color.Yellow; + public override object StatusKey { get; } = ParticleAcceleratorWireStatus.Power; + + public override StatusLightState? GetLightState(Wire wire, ParticleAcceleratorControlBoxComponent component) + { + if (!component.CanBeEnabled) + return StatusLightState.Off; + return component.Enabled ? StatusLightState.On : StatusLightState.BlinkingSlow; + } + + public override bool Cut(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + var paSystem = EntityManager.System(); + var userSession = EntityManager.TryGetComponent(user, out var actor) ? actor.PlayerSession : null; + + controller.CanBeEnabled = false; + paSystem.SwitchOff(wire.Owner, userSession, controller); + return true; + } + + public override bool Mend(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + controller.CanBeEnabled = true; + return true; + } + + public override void Pulse(EntityUid user, Wire wire, ParticleAcceleratorControlBoxComponent controller) + { + var paSystem = EntityManager.System(); + var userSession = EntityManager.TryGetComponent(user, out var actor) ? actor.PlayerSession : null; + + if (controller.Enabled) + paSystem.SwitchOff(wire.Owner, userSession, controller); + else if (controller.Assembled) + paSystem.SwitchOn(wire.Owner, userSession, controller); + } +} diff --git a/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs b/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs index 6db478dead..3ae648f2e4 100644 --- a/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs +++ b/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs @@ -7,9 +7,9 @@ namespace Content.Server.Singularity.EntitySystems; public sealed class SingularityGeneratorSystem : EntitySystem { -#region Dependencies + #region Dependencies [Dependency] private readonly IViewVariablesManager _vvm = default!; -#endregion Dependencies + #endregion Dependencies public override void Initialize() { @@ -40,29 +40,32 @@ public sealed class SingularityGeneratorSystem : EntitySystem /// The state of the singularity generator. private void OnPassThreshold(EntityUid uid, SingularityGeneratorComponent? comp) { - if(!Resolve(uid, ref comp)) + if (!Resolve(uid, ref comp)) return; - SetPower(comp, 0); - EntityManager.SpawnEntity(comp.SpawnPrototype, Transform(comp.Owner).Coordinates); + SetPower(uid, 0, comp); + EntityManager.SpawnEntity(comp.SpawnPrototype, Transform(uid).Coordinates); } -#region Getters/Setters + #region Getters/Setters /// /// Setter for /// If the singularity generator passes its threshold it also spawns a singularity. /// /// The singularity generator component. /// The new power level for the generator component to have. - public void SetPower(SingularityGeneratorComponent comp, float value) + public void SetPower(EntityUid uid, float value, SingularityGeneratorComponent? comp = null) { + if (!Resolve(uid, ref comp)) + return; + var oldValue = comp.Power; if (value == oldValue) return; comp.Power = value; if (comp.Power >= comp.Threshold && oldValue < comp.Threshold) - OnPassThreshold(comp.Owner, comp); + OnPassThreshold(uid, comp); } /// @@ -71,48 +74,22 @@ public sealed class SingularityGeneratorSystem : EntitySystem /// /// The singularity generator component. /// The new threshold power level for the generator component to have. - public void SetThreshold(SingularityGeneratorComponent comp, float value) + public void SetThreshold(EntityUid uid, float value, SingularityGeneratorComponent? comp = null) { + if (!Resolve(uid, ref comp)) + return; + var oldValue = comp.Threshold; if (value == comp.Threshold) return; comp.Power = value; if (comp.Power >= comp.Threshold && comp.Power < oldValue) - OnPassThreshold(comp.Owner, comp); - } -#region VV - /// - /// VV setter for - /// If the singularity generator passes its threshold it also spawns a singularity. - /// - /// The entity hosting the singularity generator that is being modified. - /// The value of the new power level the singularity generator should have. - /// The singularity generator to change the power level of. - public void SetPower(EntityUid uid, float value, SingularityGeneratorComponent? comp) - { - if(!Resolve(uid, ref comp)) - return; - SetPower(comp, value); + OnPassThreshold(uid, comp); } + #endregion Getters/Setters - /// - /// VV setter for - /// If the singularity generator has passed its new threshold it also spawns a singularity. - /// - /// The entity hosting the singularity generator that is being modified. - /// The value of the new threshold power level the singularity generator should have. - /// The singularity generator to change the threshold power level of. - public void SetThreshold(EntityUid uid, float value, SingularityGeneratorComponent? comp) - { - if(!Resolve(uid, ref comp)) - return; - SetThreshold(comp, value); - } -#endregion VV -#endregion Getters/Setters - -#region Event Handlers + #region Event Handlers /// /// Handles PA Particles colliding with a singularity generator. /// Adds the power from the particles to the generator. @@ -123,21 +100,23 @@ public sealed class SingularityGeneratorSystem : EntitySystem /// The state of the beginning of the collision. private void HandleParticleCollide(EntityUid uid, ParticleProjectileComponent component, ref StartCollideEvent args) { - if (EntityManager.TryGetComponent(args.OtherEntity, out var singularityGeneratorComponent)) + if (EntityManager.TryGetComponent(args.OtherEntity, out var singularityGeneratorComponent)) { SetPower( - singularityGeneratorComponent, + args.OtherEntity, singularityGeneratorComponent.Power + component.State switch - { - ParticleAcceleratorPowerState.Standby => 0, - ParticleAcceleratorPowerState.Level0 => 1, - ParticleAcceleratorPowerState.Level1 => 2, - ParticleAcceleratorPowerState.Level2 => 4, - ParticleAcceleratorPowerState.Level3 => 8, - _ => 0 - }); + { + ParticleAcceleratorPowerState.Standby => 0, + ParticleAcceleratorPowerState.Level0 => 1, + ParticleAcceleratorPowerState.Level1 => 2, + ParticleAcceleratorPowerState.Level2 => 4, + ParticleAcceleratorPowerState.Level3 => 8, + _ => 0 + }, + singularityGeneratorComponent + ); EntityManager.QueueDeleteEntity(uid); } } -#endregion Event Handlers + #endregion Event Handlers } diff --git a/Content.Server/Singularity/StartSingularityEngineCommand.cs b/Content.Server/Singularity/StartSingularityEngineCommand.cs index c4650a77b3..e63a7467e0 100644 --- a/Content.Server/Singularity/StartSingularityEngineCommand.cs +++ b/Content.Server/Singularity/StartSingularityEngineCommand.cs @@ -1,5 +1,6 @@ using Content.Server.Administration; using Content.Server.ParticleAccelerator.Components; +using Content.Server.ParticleAccelerator.EntitySystems; using Content.Server.Singularity.Components; using Content.Server.Singularity.EntitySystems; using Content.Shared.Administration; @@ -44,12 +45,18 @@ namespace Content.Server.Singularity } // Setup PA - foreach (var comp in entityManager.EntityQuery()) + var paSystem = entitySystemManager.GetEntitySystem(); + var paQuery = entityManager.EntityQueryEnumerator(); + while (paQuery.MoveNext(out var paId, out var paControl)) { - comp.RescanParts(); - comp.SetStrength(ParticleAcceleratorPowerState.Level0); - comp.SwitchOn(); + paSystem.RescanParts(paId, controller: paControl); + if (!paControl.Assembled) + continue; + + paSystem.SetStrength(paId, ParticleAcceleratorPowerState.Level0, comp: paControl); + paSystem.SwitchOn(paId, comp: paControl); } + shell.WriteLine("Done!"); } } diff --git a/Content.Shared/Singularity/Components/SharedParticleAcceleratorComponent.cs b/Content.Shared/Singularity/Components/SharedParticleAcceleratorComponent.cs index ce747aafc9..19ed21c853 100644 --- a/Content.Shared/Singularity/Components/SharedParticleAcceleratorComponent.cs +++ b/Content.Shared/Singularity/Components/SharedParticleAcceleratorComponent.cs @@ -22,13 +22,13 @@ namespace Content.Shared.Singularity.Components } [NetSerializable, Serializable] - public enum ParticleAcceleratorPowerState + public enum ParticleAcceleratorPowerState : byte { Standby = ParticleAcceleratorVisualState.Powered, Level0 = ParticleAcceleratorVisualState.Level0, Level1 = ParticleAcceleratorVisualState.Level1, Level2 = ParticleAcceleratorVisualState.Level2, - Level3 = ParticleAcceleratorVisualState.Level3 + Level3 = ParticleAcceleratorVisualState.Level3, } public enum ParticleAcceleratorVisualLayers @@ -56,9 +56,9 @@ namespace Content.Shared.Singularity.Components public int PowerReceive; //dont need a bool for the controlbox because... this is sent to the controlbox :D - public bool EmitterLeftExists; - public bool EmitterCenterExists; - public bool EmitterRightExists; + public bool EmitterStarboardExists; + public bool EmitterForeExists; + public bool EmitterPortExists; public bool PowerBoxExists; public bool FuelChamberExists; public bool EndCapExists; @@ -67,16 +67,16 @@ namespace Content.Shared.Singularity.Components public ParticleAcceleratorPowerState MaxLevel; public bool WirePowerBlock; - public ParticleAcceleratorUIState(bool assembled, bool enabled, ParticleAcceleratorPowerState state, int powerReceive, int powerDraw, bool emitterLeftExists, bool emitterCenterExists, bool emitterRightExists, bool powerBoxExists, bool fuelChamberExists, bool endCapExists, bool interfaceBlock, ParticleAcceleratorPowerState maxLevel, bool wirePowerBlock) + public ParticleAcceleratorUIState(bool assembled, bool enabled, ParticleAcceleratorPowerState state, int powerReceive, int powerDraw, bool emitterStarboardExists, bool emitterForeExists, bool emitterPortExists, bool powerBoxExists, bool fuelChamberExists, bool endCapExists, bool interfaceBlock, ParticleAcceleratorPowerState maxLevel, bool wirePowerBlock) { Assembled = assembled; Enabled = enabled; State = state; PowerDraw = powerDraw; PowerReceive = powerReceive; - EmitterLeftExists = emitterLeftExists; - EmitterCenterExists = emitterCenterExists; - EmitterRightExists = emitterRightExists; + EmitterStarboardExists = emitterStarboardExists; + EmitterForeExists = emitterForeExists; + EmitterPortExists = emitterPortExists; PowerBoxExists = powerBoxExists; FuelChamberExists = fuelChamberExists; EndCapExists = endCapExists; diff --git a/Resources/Locale/en-US/wires/wire-names.ftl b/Resources/Locale/en-US/wires/wire-names.ftl index 642f6f45ed..1a4fc5b741 100644 --- a/Resources/Locale/en-US/wires/wire-names.ftl +++ b/Resources/Locale/en-US/wires/wire-names.ftl @@ -14,3 +14,7 @@ wire-name-power = POWR wire-name-arcade-invincible = MNGR wire-name-vending-contraband = MNGR wire-name-vending-eject = VEND +wire-name-pa-keyboard = KEYB +wire-name-pa-limiter = LIMT +wire-name-pa-power = POWR +wire-name-pa-strength = STRC diff --git a/Resources/Prototypes/Catalog/Fills/Crates/engines.yml b/Resources/Prototypes/Catalog/Fills/Crates/engines.yml index be1cbb70da..a3cbe17ac7 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/engines.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/engines.yml @@ -69,9 +69,9 @@ - type: StorageFill contents: - id: MachineParticleAcceleratorEndCapCircuitboard - - id: MachineParticleAcceleratorEmitterLeftCircuitboard - - id: MachineParticleAcceleratorEmitterCenterCircuitboard - - id: MachineParticleAcceleratorEmitterRightCircuitboard + - id: MachineParticleAcceleratorEmitterStarboardCircuitboard + - id: MachineParticleAcceleratorEmitterForeCircuitboard + - id: MachineParticleAcceleratorEmitterPortCircuitboard - id: MachineParticleAcceleratorFuelChamberCircuitboard - id: MachineParticleAcceleratorPowerBoxCircuitboard - id: ParticleAcceleratorComputerCircuitboard diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/particle_accelerator.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/particle_accelerator.yml index 865050d23c..eb040c0bc7 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/particle_accelerator.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/particle_accelerator.yml @@ -48,43 +48,43 @@ # Emitter - type: entity - id: MachineParticleAcceleratorEmitterLeftCircuitboard + id: MachineParticleAcceleratorEmitterStarboardCircuitboard parent: BaseMachineCircuitboard - name: PA emitter left board + name: PA starboard emitter board description: A machine board for a particle accelerator left emitter components: - type: Sprite state: engineering - type: MachineBoard - prototype: ParticleAcceleratorEmitterLeftUnfinished + prototype: ParticleAcceleratorEmitterStarboardUnfinished materialRequirements: Glass: 5 Steel: 5 - type: entity - id: MachineParticleAcceleratorEmitterCenterCircuitboard + id: MachineParticleAcceleratorEmitterForeCircuitboard parent: BaseMachineCircuitboard - name: PA emitter center board + name: PA fore emitter board description: A machine board for a particle accelerator center emitter components: - type: Sprite state: engineering - type: MachineBoard - prototype: ParticleAcceleratorEmitterCenterUnfinished + prototype: ParticleAcceleratorEmitterForeUnfinished materialRequirements: Glass: 5 Steel: 5 - type: entity - id: MachineParticleAcceleratorEmitterRightCircuitboard + id: MachineParticleAcceleratorEmitterPortCircuitboard parent: BaseMachineCircuitboard - name: PA emitter right board + name: PA port emitter board description: A machine board for a particle accelerator right emitter components: - type: Sprite state: engineering - type: MachineBoard - prototype: ParticleAcceleratorEmitterRightUnfinished + prototype: ParticleAcceleratorEmitterPortUnfinished materialRequirements: Glass: 5 Steel: 5 diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/base_particleaccelerator.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/base_particleaccelerator.yml index a2a3687f9c..01c85a184e 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/base_particleaccelerator.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/base_particleaccelerator.yml @@ -4,30 +4,30 @@ placement: mode: SnapgridCenter components: - - type: InteractionOutline - - type: Anchorable - - type: Rotatable - - type: Physics - bodyType: Static - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.45,-0.45,0.45,0.45" - density: 190 - mask: - - MachineMask - layer: - - MachineLayer - - type: Transform - anchored: true - noRot: false - - type: Pullable - - type: Clickable - - type: GuideHelp - guides: [ Singularity, Power ] - - type: Appearance + - type: InteractionOutline + - type: Anchorable + - type: Rotatable + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.45,-0.45,0.45,0.45" + density: 190 + mask: + - MachineMask + layer: + - MachineLayer + - type: Transform + anchored: true + noRot: false + - type: Pullable + - type: Clickable + - type: GuideHelp + guides: [ Singularity, Power ] + - type: Appearance - type: entity id: ParticleAcceleratorFinishedPart diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/control_box.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/control_box.yml index 12e46a7baa..5eb45c5997 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/control_box.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/control_box.yml @@ -1,42 +1,31 @@ - type: entity - parent: ParticleAcceleratorBase + parent: ParticleAcceleratorFinishedPart id: ParticleAcceleratorControlBox name: PA control computer description: This controls the density of the particles. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/control_box.rsi - layers: - - state: completed - map: [ "enum.ParticleAcceleratorVisualLayers.Base" ] - - state: unlitp - map: [ "enum.ParticleAcceleratorVisualLayers.Unlit" ] - shader: unshaded - visible: false - - type: Appearance - - type: ParticleAcceleratorPartVisuals - stateBase: unlit - - type: ApcPowerReceiver - powerLoad: 250 - - type: ExtensionCableReceiver - - type: ParticleAcceleratorControlBox - - type: Construction - graph: ParticleAcceleratorControlBox - node: completed - - type: ActivatableUI - key: enum.ParticleAcceleratorControlBoxUiKey.Key - - type: ActivatableUIRequiresPower - - type: UserInterface - interfaces: - - key: enum.ParticleAcceleratorControlBoxUiKey.Key - type: ParticleAcceleratorBoundUserInterface - - key: enum.WiresUiKey.Key - type: WiresBoundUserInterface - - type: WiresPanel - - type: Wires - BoardName: "Mk2 Particle Accelerator" - LayoutId: ParticleAccelerator - + - type: Sprite + sprite: Structures/Power/Generation/PA/control_box.rsi + - type: ParticleAcceleratorControlBox + - type: ApcPowerReceiver + powerLoad: 250 + - type: ExtensionCableReceiver + - type: Construction + graph: ParticleAcceleratorControlBox + - type: ActivatableUI + key: enum.ParticleAcceleratorControlBoxUiKey.Key + - type: ActivatableUIRequiresPower + - type: UserInterface + interfaces: + - key: enum.ParticleAcceleratorControlBoxUiKey.Key + type: ParticleAcceleratorBoundUserInterface + - key: enum.WiresUiKey.Key + type: WiresBoundUserInterface + - type: WiresPanel + - type: Wires + BoardName: "Mk2 Particle Accelerator" + LayoutId: ParticleAccelerator + # Unfinished - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/emitter.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/emitter.yml index f89d38e6b0..43b4d5f9ef 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/emitter.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/emitter.yml @@ -1,76 +1,76 @@ - type: entity parent: ParticleAcceleratorFinishedPart - id: ParticleAcceleratorEmitterLeft - name: PA containment emitter L + id: ParticleAcceleratorEmitterPort + name: PA port containment emitter description: This launchs the Alpha particles, might not want to stand near this end. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/emitter_left.rsi - - type: ParticleAcceleratorEmitter - emitterType: Left - - type: Construction - graph: ParticleAcceleratorEmitterLeft + - type: Sprite + sprite: Structures/Power/Generation/PA/emitter_port.rsi + - type: ParticleAcceleratorEmitter + emitterType: Port + - type: Construction + graph: ParticleAcceleratorEmitterPort - type: entity parent: ParticleAcceleratorFinishedPart - id: ParticleAcceleratorEmitterCenter - name: PA containment emitter C + id: ParticleAcceleratorEmitterFore + name: PA fore containment emitter description: This launchs the Alpha particles, might not want to stand near this end. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/emitter_center.rsi - - type: ParticleAcceleratorEmitter - emitterType: Center - - type: Construction - graph: ParticleAcceleratorEmitterCenter + - type: Sprite + sprite: Structures/Power/Generation/PA/emitter_fore.rsi + - type: ParticleAcceleratorEmitter + emitterType: Fore + - type: Construction + graph: ParticleAcceleratorEmitterFore - type: entity parent: ParticleAcceleratorFinishedPart - id: ParticleAcceleratorEmitterRight - name: PA containment emitter R + id: ParticleAcceleratorEmitterStarboard + name: PA starboard containment emitter description: This launchs the Alpha particles, might not want to stand near this end. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/emitter_right.rsi - - type: ParticleAcceleratorEmitter - emitterType: Right - - type: Construction - graph: ParticleAcceleratorEmitterRight + - type: Sprite + sprite: Structures/Power/Generation/PA/emitter_starboard.rsi + - type: ParticleAcceleratorEmitter + emitterType: Starboard + - type: Construction + graph: ParticleAcceleratorEmitterStarboard # Unfinished - type: entity parent: ParticleAcceleratorUnfinishedBase - id: ParticleAcceleratorEmitterLeftUnfinished - name: PA containment emitter L - suffix: Unfinished, Left + id: ParticleAcceleratorEmitterPortUnfinished + name: PA port containment emitter + suffix: Unfinished, Port description: This launchs the Alpha particles, might not want to stand near this end. It looks unfinished. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/emitter_left.rsi - - type: Construction - graph: ParticleAcceleratorEmitterLeft + - type: Sprite + sprite: Structures/Power/Generation/PA/emitter_port.rsi + - type: Construction + graph: ParticleAcceleratorEmitterPort - type: entity parent: ParticleAcceleratorUnfinishedBase - id: ParticleAcceleratorEmitterCenterUnfinished - name: PA containment emitter C - suffix: Unfinished + id: ParticleAcceleratorEmitterForeUnfinished + name: PA fore containment emitter + suffix: Unfinished, Fore description: This launchs the Alpha particles, might not want to stand near this end. It looks unfinished. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/emitter_center.rsi - - type: Construction - graph: ParticleAcceleratorEmitterCenter + - type: Sprite + sprite: Structures/Power/Generation/PA/emitter_fore.rsi + - type: Construction + graph: ParticleAcceleratorEmitterFore - type: entity parent: ParticleAcceleratorUnfinishedBase - id: ParticleAcceleratorEmitterRightUnfinished - name: PA containment emitter R - suffix: Unfinished + id: ParticleAcceleratorEmitterStarboardUnfinished + name: PA starboard containment emitter + suffix: Unfinished, Starboard description: This launchs the Alpha particles, might not want to stand near this end. It looks unfinished. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/emitter_right.rsi - - type: Construction - graph: ParticleAcceleratorEmitterRight + - type: Sprite + sprite: Structures/Power/Generation/PA/emitter_starboard.rsi + - type: Construction + graph: ParticleAcceleratorEmitterStarboard diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/end_cap.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/end_cap.yml index cd43d3bd36..80b0240d89 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/end_cap.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/end_cap.yml @@ -4,14 +4,14 @@ name: PA end-cap description: Formally known as the Alpha Particle Generation Array. This is where Alpha particles are generated from [REDACTED]. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/end_cap.rsi - layers: - - state: completed - map: [ "enum.ParticleAcceleratorVisualLayers.Base" ] - - type: ParticleAcceleratorEndCap - - type: Construction - graph: ParticleAcceleratorEndCap + - type: Sprite + sprite: Structures/Power/Generation/PA/end_cap.rsi + layers: + - state: completed + map: [ "enum.ParticleAcceleratorVisualLayers.Base" ] + - type: ParticleAcceleratorEndCap + - type: Construction + graph: ParticleAcceleratorEndCap # Unfinished @@ -22,7 +22,7 @@ suffix: Unfinished description: Formally known as the Alpha Particle Generation Array. This is where Alpha particles are generated from [REDACTED]. It looks unfinished. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/end_cap.rsi - - type: Construction - graph: ParticleAcceleratorEndCap + - type: Sprite + sprite: Structures/Power/Generation/PA/end_cap.rsi + - type: Construction + graph: ParticleAcceleratorEndCap diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/fuel_chamber.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/fuel_chamber.yml index d835895b01..868e9cf8f9 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/fuel_chamber.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/fuel_chamber.yml @@ -4,11 +4,11 @@ name: PA fuel chamber description: Formally known as the EM Acceleration Chamber. This is where the Alpha particles are accelerated to radical speeds. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/fuel_chamber.rsi - - type: ParticleAcceleratorFuelChamber - - type: Construction - graph: ParticleAcceleratorFuelChamber + - type: Sprite + sprite: Structures/Power/Generation/PA/fuel_chamber.rsi + - type: ParticleAcceleratorFuelChamber + - type: Construction + graph: ParticleAcceleratorFuelChamber # Unfinished @@ -19,7 +19,7 @@ suffix: Unfinished description: Formally known as the EM Acceleration Chamber. This is where the Alpha particles are accelerated to radical speeds. It looks unfinished. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/fuel_chamber.rsi - - type: Construction - graph: ParticleAcceleratorFuelChamber + - type: Sprite + sprite: Structures/Power/Generation/PA/fuel_chamber.rsi + - type: Construction + graph: ParticleAcceleratorFuelChamber diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml index d4729fa13f..3c0b0d6b0b 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml @@ -34,6 +34,8 @@ - type: ParticleProjectile - type: SinguloFood # Energy is setup by the PA particle fire function. + - type: TimedDespawn + lifetime: 3.0 - type: Appearance - type: GenericVisualizer visuals: diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/power_box.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/power_box.yml index 93a4b4b029..cb2f51475a 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/power_box.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/power_box.yml @@ -4,19 +4,19 @@ name: PA power box description: Formally known as the Particle Focusing EM Lens. This uses electromagnetic waves to focus the Alpha-Particles. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/power_box.rsi - - type: ParticleAcceleratorPowerBox - - type: PowerConsumer - voltage: High - - type: NodeContainer - examinable: true - nodes: - input: - !type:CableDeviceNode - nodeGroupID: HVPower - - type: Construction - graph: ParticleAcceleratorPowerBox + - type: Sprite + sprite: Structures/Power/Generation/PA/power_box.rsi + - type: ParticleAcceleratorPowerBox + - type: PowerConsumer + voltage: High + - type: NodeContainer + examinable: true + nodes: + input: + !type:CableDeviceNode + nodeGroupID: HVPower + - type: Construction + graph: ParticleAcceleratorPowerBox - type: entity parent: ParticleAcceleratorUnfinishedBase @@ -25,8 +25,7 @@ suffix: Unfinished description: Formally known as the Particle Focusing EM Lens. This uses electromagnetic waves to focus the Alpha-Particles. It looks unfinished. components: - - type: Sprite - sprite: Structures/Power/Generation/PA/power_box.rsi - - type: Construction - graph: ParticleAcceleratorPowerBox - + - type: Sprite + sprite: Structures/Power/Generation/PA/power_box.rsi + - type: Construction + graph: ParticleAcceleratorPowerBox diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/particle_accelerator.yml b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/particle_accelerator.yml index 1c8c2e75fc..7d6792f191 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/particle_accelerator.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/particle_accelerator.yml @@ -197,11 +197,11 @@ doAfter: 0.5 - type: constructionGraph - id: ParticleAcceleratorEmitterLeft + id: ParticleAcceleratorEmitterPort start: start graph: - node: start - entity: ParticleAcceleratorEmitterLeftUnfinished + entity: ParticleAcceleratorEmitterPortUnfinished actions: - !type:AppearanceChange edges: @@ -213,7 +213,7 @@ doAfter: 0.5 - node: wired - entity: ParticleAcceleratorEmitterLeftUnfinished + entity: ParticleAcceleratorEmitterPortUnfinished actions: - !type:AppearanceChange edges: @@ -236,7 +236,7 @@ doAfter: 0.5 - node: completed - entity: ParticleAcceleratorEmitterLeft + entity: ParticleAcceleratorEmitterPort edges: - to: wired conditions: @@ -246,11 +246,11 @@ doAfter: 0.5 - type: constructionGraph - id: ParticleAcceleratorEmitterCenter + id: ParticleAcceleratorEmitterFore start: start graph: - node: start - entity: ParticleAcceleratorEmitterCenterUnfinished + entity: ParticleAcceleratorEmitterForeUnfinished actions: - !type:AppearanceChange edges: @@ -262,7 +262,7 @@ doAfter: 0.5 - node: wired - entity: ParticleAcceleratorEmitterCenterUnfinished + entity: ParticleAcceleratorEmitterForeUnfinished actions: - !type:AppearanceChange edges: @@ -285,7 +285,7 @@ doAfter: 0.5 - node: completed - entity: ParticleAcceleratorEmitterCenter + entity: ParticleAcceleratorEmitterFore edges: - to: wired conditions: @@ -295,11 +295,11 @@ doAfter: 0.5 - type: constructionGraph - id: ParticleAcceleratorEmitterRight + id: ParticleAcceleratorEmitterStarboard start: start graph: - node: start - entity: ParticleAcceleratorEmitterRightUnfinished + entity: ParticleAcceleratorEmitterStarboardUnfinished actions: - !type:AppearanceChange edges: @@ -311,7 +311,7 @@ doAfter: 0.5 - node: wired - entity: ParticleAcceleratorEmitterRightUnfinished + entity: ParticleAcceleratorEmitterStarboardUnfinished actions: - !type:AppearanceChange edges: @@ -334,7 +334,7 @@ doAfter: 0.5 - node: completed - entity: ParticleAcceleratorEmitterRight + entity: ParticleAcceleratorEmitterStarboard edges: - to: wired conditions: diff --git a/Resources/Prototypes/Wires/layouts.yml b/Resources/Prototypes/Wires/layouts.yml index e130b561ca..be0bb14b93 100644 --- a/Resources/Prototypes/Wires/layouts.yml +++ b/Resources/Prototypes/Wires/layouts.yml @@ -75,3 +75,12 @@ wires: - !type:PowerWireAction - !type:CryoPodEjectLockWireAction + +- type: wireLayout + id: ParticleAccelerator + dummyWires: 1 + wires: + - !type:ParticleAcceleratorKeyboardWireAction + - !type:ParticleAcceleratorLimiterWireAction + - !type:ParticleAcceleratorPowerWireAction + - !type:ParticleAcceleratorStrengthWireAction diff --git a/Resources/ServerInfo/Guidebook/Singularity.xml b/Resources/ServerInfo/Guidebook/Singularity.xml index efe0d37c1b..a40ef7f43e 100644 --- a/Resources/ServerInfo/Guidebook/Singularity.xml +++ b/Resources/ServerInfo/Guidebook/Singularity.xml @@ -48,9 +48,9 @@ They connect to HV cables and generate power from nearby radiation sources when - - - + + + The Particle Accelerator (PA) is a multi-tile structure that launches acclerated particles from its emitters. Its emitters should always face the gravitational singularity generator. diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/completed.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/completed.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/completed.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/completed.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/meta.json b/Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/meta.json similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/meta.json rename to Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/meta.json diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unlitp.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unlitp.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unlitp.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unlitp.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unlitp0.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unlitp0.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unlitp0.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unlitp0.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unlitp1.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unlitp1.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unlitp1.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unlitp1.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unlitp2.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unlitp2.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unlitp2.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unlitp2.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unlitp3.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unlitp3.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unlitp3.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unlitp3.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unwired.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unwired.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/unwired.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/unwired.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/wired.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/wired.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_center.rsi/wired.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_fore.rsi/wired.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/completed.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/completed.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/completed.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/completed.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/meta.json b/Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/meta.json similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/meta.json rename to Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/meta.json diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unlitp.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unlitp.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unlitp.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unlitp.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unlitp0.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unlitp0.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unlitp0.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unlitp0.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unlitp1.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unlitp1.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unlitp1.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unlitp1.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unlitp2.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unlitp2.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unlitp2.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unlitp2.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unlitp3.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unlitp3.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unlitp3.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unlitp3.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unwired.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unwired.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unwired.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/unwired.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/wired.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/wired.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/wired.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_port.rsi/wired.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/completed.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/completed.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/completed.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/completed.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/meta.json b/Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/meta.json similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/meta.json rename to Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/meta.json diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unlitp.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unlitp.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unlitp.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unlitp.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unlitp0.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unlitp0.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_right.rsi/unlitp0.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unlitp0.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unlitp1.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unlitp1.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unlitp1.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unlitp1.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unlitp2.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unlitp2.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unlitp2.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unlitp2.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unlitp3.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unlitp3.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unlitp3.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unlitp3.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unwired.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unwired.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/unwired.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/unwired.png diff --git a/Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/wired.png b/Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/wired.png similarity index 100% rename from Resources/Textures/Structures/Power/Generation/PA/emitter_left.rsi/wired.png rename to Resources/Textures/Structures/Power/Generation/PA/emitter_starboard.rsi/wired.png diff --git a/Resources/migration.yml b/Resources/migration.yml index 8960b593c3..a9b824da7c 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -60,3 +60,17 @@ OrGate: null IHSVoidsuit: null ClothingHeadHelmetIHSVoidHelm: null ClothingHandsGlovesIhscombat: null + +# 2023-06-02 +# Yes, this is right. They were, in fact, reversed because the default orientation of the particle accelerator was _down_ relative to the screen. +# This resulted in the parts being named assuming that the person building the accelerator treated it like a rocket with the particles coming out the _back_. +# As this was confusing they were converted to nautical orientation with forward being towards the emitters. +MachineParticleAcceleratorEmitterCenterCircuitboard: MachineParticleAcceleratorEmitterForeCircuitboard +MachineParticleAcceleratorEmitterLeftCircuitboard: MachineParticleAcceleratorEmitterStarboardCircuitboard +MachineParticleAcceleratorEmitterRightCircuitboard: MachineParticleAcceleratorEmitterPortCircuitboard +ParticleAcceleratorEmitterCenter: ParticleAcceleratorEmitterFore +ParticleAcceleratorEmitterCenterUnfinished: ParticleAcceleratorEmitterForeUnfinished +ParticleAcceleratorEmitterLeft: ParticleAcceleratorEmitterStarboard +ParticleAcceleratorEmitterLeftUnfinished: ParticleAcceleratorEmitterStarboardUnfinished +ParticleAcceleratorEmitterRight: ParticleAcceleratorEmitterPort +ParticleAcceleratorEmitterRightUnfinished: ParticleAcceleratorEmitterPortUnfinished