diff --git a/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs b/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs index 5612a71f67..d74f8d30e7 100644 --- a/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs +++ b/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs @@ -82,7 +82,10 @@ namespace Content.Client.GameObjects.Components.HUD.Inventory foreach (var (slot, entityUid) in cast.Entities) { - var entity = Owner.EntityManager.GetEntity(entityUid); + if (!Owner.EntityManager.TryGetEntity(entityUid, out var entity)) + { + continue; + } if (!_slots.ContainsKey(slot) || _slots[slot] != entity) { _slots[slot] = entity; diff --git a/Content.Client/GameObjects/Components/Nutrition/DrinkFoodVisualizer.cs b/Content.Client/GameObjects/Components/Nutrition/DrinkFoodVisualizer.cs index caef972db0..214c8d9cef 100644 --- a/Content.Client/GameObjects/Components/Nutrition/DrinkFoodVisualizer.cs +++ b/Content.Client/GameObjects/Components/Nutrition/DrinkFoodVisualizer.cs @@ -22,7 +22,11 @@ namespace Content.Client.GameObjects.Components.Nutrition public override void OnChangeData(AppearanceComponent component) { - var sprite = component.Owner.GetComponent(); + if(!component.Owner.TryGetComponent(out var sprite)) + { + return; + }; + if (!component.TryGetData(SharedFoodComponent.FoodVisuals.MaxUses, out var maxUses)) { return; diff --git a/Content.Client/GameObjects/Components/ParticleAcceleratorPartVisualizer.cs b/Content.Client/GameObjects/Components/ParticleAcceleratorPartVisualizer.cs new file mode 100644 index 0000000000..2e992a516f --- /dev/null +++ b/Content.Client/GameObjects/Components/ParticleAcceleratorPartVisualizer.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Singularity; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Reflection; +using Robust.Shared.Interfaces.Serialization; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; +using YamlDotNet.RepresentationModel; + +namespace Content.Client.GameObjects.Components +{ + public class ParticleAcceleratorPartVisualizer : AppearanceVisualizer + { + private Dictionary _states = new Dictionary(); + + public override void LoadData(YamlMappingNode node) + { + base.LoadData(node); + + var serializer = YamlObjectSerializer.NewReader(node); + if (!serializer.TryReadDataField("baseState", out var baseState)) + { + throw new PrototypeLoadException("No baseState property specified for ParticleAcceleratorPartVisualizer"); + } + + _states.Add(ParticleAcceleratorVisualState.Powered, baseState+"p"); + _states.Add(ParticleAcceleratorVisualState.Level0, baseState+"p0"); + _states.Add(ParticleAcceleratorVisualState.Level1, baseState+"p1"); + _states.Add(ParticleAcceleratorVisualState.Level2, baseState+"p2"); + _states.Add(ParticleAcceleratorVisualState.Level3, baseState+"p3"); + } + + public override void InitializeEntity(IEntity entity) + { + base.InitializeEntity(entity); + if (!entity.TryGetComponent(out var sprite)) + { + throw new EntityCreationException("No sprite component found in entity that has ParticleAcceleratorPartVisualizer"); + } + + if (!sprite.AllLayers.Any()) + { + throw new EntityCreationException("No Layer set for entity that has ParticleAcceleratorPartVisualizer"); + } + } + + public override void OnChangeData(AppearanceComponent component) + { + if (component.Owner.Deleted) + return; + + if (!component.Owner.TryGetComponent(out var sprite)) return; + if (!component.TryGetData(ParticleAcceleratorVisuals.VisualState, out ParticleAcceleratorVisualState state)) + { + state = ParticleAcceleratorVisualState.Unpowered; + } + + if (state != ParticleAcceleratorVisualState.Unpowered) + { + sprite.LayerSetVisible(ParticleAcceleratorVisualLayers.Unlit, true); + sprite.LayerSetState(ParticleAcceleratorVisualLayers.Unlit, _states[state]); + } + else + { + sprite.LayerSetVisible(ParticleAcceleratorVisualLayers.Unlit, false); + } + } + } +} diff --git a/Content.Client/GameObjects/Components/RadiationCollectorVisualizer.cs b/Content.Client/GameObjects/Components/RadiationCollectorVisualizer.cs new file mode 100644 index 0000000000..07f4738f47 --- /dev/null +++ b/Content.Client/GameObjects/Components/RadiationCollectorVisualizer.cs @@ -0,0 +1,104 @@ +using System; +using Content.Client.GameObjects.Components.Doors; +using Content.Client.GameObjects.Components.Wires; +using Content.Shared.GameObjects.Components.Doors; +using Content.Shared.GameObjects.Components.Singularity; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Client.GameObjects.Components.Animations; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using YamlDotNet.RepresentationModel; + +namespace Content.Client.GameObjects.Components +{ + public class RadiationCollectorVisualizer : AppearanceVisualizer + { + private const string AnimationKey = "radiationcollector_animation"; + + private Animation ActivateAnimation; + private Animation DeactiveAnimation; + + public override void LoadData(YamlMappingNode node) + { + ActivateAnimation = new Animation {Length = TimeSpan.FromSeconds(0.8f)}; + { + var flick = new AnimationTrackSpriteFlick(); + ActivateAnimation.AnimationTracks.Add(flick); + flick.LayerKey = RadiationCollectorVisualLayers.Main; + flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("ca_active", 0f)); + + /*var sound = new AnimationTrackPlaySound(); + CloseAnimation.AnimationTracks.Add(sound); + sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(closeSound, 0));*/ + } + + DeactiveAnimation = new Animation {Length = TimeSpan.FromSeconds(0.8f)}; + { + var flick = new AnimationTrackSpriteFlick(); + DeactiveAnimation.AnimationTracks.Add(flick); + flick.LayerKey = RadiationCollectorVisualLayers.Main; + flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("ca_deactive", 0f)); + + /*var sound = new AnimationTrackPlaySound(); + CloseAnimation.AnimationTracks.Add(sound); + sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(closeSound, 0));*/ + } + } + + public override void InitializeEntity(IEntity entity) + { + if (!entity.HasComponent()) + { + entity.AddComponent(); + } + } + + + public override void OnChangeData(AppearanceComponent component) + { + if (component.Owner.Deleted) + return; + + if (!component.Owner.TryGetComponent(out var sprite)) return; + if (!component.Owner.TryGetComponent(out var animPlayer)) return; + if (!component.TryGetData(RadiationCollectorVisuals.VisualState, out RadiationCollectorVisualState state)) + { + state = RadiationCollectorVisualState.Deactive; + } + + switch (state) + { + case RadiationCollectorVisualState.Active: + sprite.LayerSetState(RadiationCollectorVisualLayers.Main, "ca_on"); + break; + case RadiationCollectorVisualState.Activating: + if (!animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Play(ActivateAnimation, AnimationKey); + animPlayer.AnimationCompleted += _ => + component.SetData(RadiationCollectorVisuals.VisualState, + RadiationCollectorVisualState.Active); + } + break; + case RadiationCollectorVisualState.Deactivating: + if (!animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Play(DeactiveAnimation, AnimationKey); + animPlayer.AnimationCompleted += _ => + component.SetData(RadiationCollectorVisuals.VisualState, + RadiationCollectorVisualState.Deactive); + } + break; + case RadiationCollectorVisualState.Deactive: + sprite.LayerSetState(RadiationCollectorVisualLayers.Main, "ca_off"); + break; + } + } + + } + public enum RadiationCollectorVisualLayers + { + Main + } +} diff --git a/Content.Client/GameObjects/Components/Singularity/ContainmentFieldComponent.cs b/Content.Client/GameObjects/Components/Singularity/ContainmentFieldComponent.cs new file mode 100644 index 0000000000..692f0b4b89 --- /dev/null +++ b/Content.Client/GameObjects/Components/Singularity/ContainmentFieldComponent.cs @@ -0,0 +1,28 @@ +using Robust.Client.GameObjects; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.Log; + +namespace Content.Client.GameObjects.Components.Singularity +{ + public class ContainmentFieldComponent : Component + { + public override string Name => "Containment Field"; + + private SpriteComponent _spriteComponent; + + public override void Initialize() + { + base.Initialize(); + + if (!Owner.TryGetComponent(out _spriteComponent)) + { + Logger.Error("Containmentfieldcomponent created without spritecomponent"); + } + else + { + _spriteComponent.Directional = false; + } + } + } +} diff --git a/Content.Client/GameObjects/Components/Singularity/EmitterVisualizer.cs b/Content.Client/GameObjects/Components/Singularity/EmitterVisualizer.cs new file mode 100644 index 0000000000..44b4d356cc --- /dev/null +++ b/Content.Client/GameObjects/Components/Singularity/EmitterVisualizer.cs @@ -0,0 +1,49 @@ +using System; +using Content.Shared.GameObjects.Components.Singularity; +using Robust.Client.GameObjects; +using Robust.Client.Interfaces.GameObjects.Components; + +namespace Content.Client.GameObjects.Components.Singularity +{ + public class EmitterVisualizer : AppearanceVisualizer + { + private const string OverlayBeam = "emitter-beam"; + private const string OverlayUnderPowered = "emitter-underpowered"; + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + if (!component.Owner.TryGetComponent(out ISpriteComponent sprite)) + { + return; + } + + if (!component.TryGetData(EmitterVisuals.Locked, out bool locked)) + locked = false; + + + if (!component.TryGetData(EmitterVisuals.VisualState, out EmitterVisualState state)) + state = EmitterVisualState.Off; + + switch (state) + { + case EmitterVisualState.On: + sprite.LayerSetVisible(1, true); + sprite.LayerSetState(1, OverlayBeam); + break; + case EmitterVisualState.Underpowered: + sprite.LayerSetVisible(1, true); + sprite.LayerSetState(1, OverlayUnderPowered); + break; + case EmitterVisualState.Off: + sprite.LayerSetVisible(1, false); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + sprite.LayerSetVisible(2, locked); + } + } +} diff --git a/Content.Client/GameObjects/Components/Sound/LoopingSoundComponent.cs b/Content.Client/GameObjects/Components/Sound/LoopingSoundComponent.cs index c9caf04338..203cf70ef8 100644 --- a/Content.Client/GameObjects/Components/Sound/LoopingSoundComponent.cs +++ b/Content.Client/GameObjects/Components/Sound/LoopingSoundComponent.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Linq; using Content.Shared.GameObjects.Components.Sound; using Content.Shared.Physics; using Robust.Client.GameObjects.EntitySystems; @@ -55,7 +57,15 @@ namespace Content.Client.GameObjects.Components.Sound { if (!schedule.Play) return; // We make sure this hasn't changed. if (_audioSystem == null) _audioSystem = EntitySystem.Get(); - _audioStreams.Add(schedule,_audioSystem.Play(schedule.Filename, Owner, schedule.AudioParams)); + + if (!_audioStreams.ContainsKey(schedule)) + { + _audioStreams.Add(schedule,_audioSystem.Play(schedule.Filename, Owner, schedule.AudioParams)); + } + else + { + _audioStreams[schedule] = _audioSystem.Play(schedule.Filename, Owner, schedule.AudioParams); + } if (schedule.Times == 0) return; diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs index 9f74eb6aec..ae8bd8da7c 100644 --- a/Content.Client/IgnoredComponents.cs +++ b/Content.Client/IgnoredComponents.cs @@ -194,6 +194,19 @@ "Internals", "GasTank", "BreathMask", + "RadiationCollector", + "ContainmentFieldGenerator", + "ContainmentField", + "Emitter", + "Singularity", + "SingularityGenerator", + "EmitterBoltComponent", + "ParticleProjectile", + "ParticleAcceleratorControlBox", + "ParticleAcceleratorEmitter", + "ParticleAcceleratorEndCap", + "ParticleAcceleratorFuelChamber", + "ParticleAcceleratorPowerBox" }; } } diff --git a/Content.Client/UserInterface/ParticleAccelerator/ParticleAcceleratorBoundUserInterface.cs b/Content.Client/UserInterface/ParticleAccelerator/ParticleAcceleratorBoundUserInterface.cs new file mode 100644 index 0000000000..1333b48546 --- /dev/null +++ b/Content.Client/UserInterface/ParticleAccelerator/ParticleAcceleratorBoundUserInterface.cs @@ -0,0 +1,52 @@ +using Content.Shared.GameObjects.Components; +using JetBrains.Annotations; +using Robust.Client.GameObjects.Components.UserInterface; +using Robust.Shared.GameObjects.Components.UserInterface; + +namespace Content.Client.ParticleAccelerator +{ + public class ParticleAcceleratorBoundUserInterface : BoundUserInterface + { + private ParticleAcceleratorControlMenu _menu; + + public ParticleAcceleratorBoundUserInterface([NotNull] ClientUserInterfaceComponent owner, [NotNull] object uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _menu = new ParticleAcceleratorControlMenu(this); + _menu.OnClose += Close; + _menu.OpenCentered(); + } + + public void SendEnableMessage(bool enable) + { + SendMessage(new ParticleAcceleratorSetEnableMessage(enable)); + } + + public void SendPowerStateMessage(ParticleAcceleratorPowerState state) + { + SendMessage(new ParticleAcceleratorSetPowerStateMessage(state)); + } + + public void SendScanPartsMessage() + { + SendMessage(new ParticleAcceleratorRescanPartsMessage()); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + _menu.DataUpdate((ParticleAcceleratorUIState) state); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + _menu.Close(); + } + } +} diff --git a/Content.Client/UserInterface/ParticleAccelerator/ParticleAcceleratorControlMenu.cs b/Content.Client/UserInterface/ParticleAccelerator/ParticleAcceleratorControlMenu.cs new file mode 100644 index 0000000000..2e9ab07227 --- /dev/null +++ b/Content.Client/UserInterface/ParticleAccelerator/ParticleAcceleratorControlMenu.cs @@ -0,0 +1,545 @@ +using System; +using Content.Client.Animations; +using Content.Client.UserInterface; +using Content.Client.UserInterface.Stylesheets; +using Content.Client.Utility; +using Content.Shared.GameObjects.Components; +using Robust.Client.Animations; +using Robust.Client.Graphics; +using Robust.Client.Graphics.Drawing; +using Robust.Client.Graphics.Shaders; +using Robust.Client.Interfaces.ResourceManagement; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Noise; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Client.ParticleAccelerator +{ + public sealed class ParticleAcceleratorControlMenu : BaseWindow + { + private ShaderInstance _greyScaleShader; + + private readonly ParticleAcceleratorBoundUserInterface Owner; + + private readonly Label _drawLabel; + private readonly NoiseGenerator _drawNoiseGenerator; + private readonly Button _onButton; + private readonly Button _offButton; + private readonly Button _scanButton; + private readonly Label _statusLabel; + private readonly SpinBox _stateSpinBox; + + private readonly VBoxContainer _alarmControl; + private readonly Animation _alarmControlAnimation; + + private readonly PASegmentControl _endCapTexture; + 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 float _time; + private int _lastDraw; + private int _lastReceive; + + private bool _blockSpinBox; + private bool _assembled; + private bool _shouldContinueAnimating; + + public ParticleAcceleratorControlMenu(ParticleAcceleratorBoundUserInterface owner) + { + _greyScaleShader = IoCManager.Resolve().Index("Greyscale").Instance(); + + Owner = owner; + _drawNoiseGenerator = new NoiseGenerator(NoiseGenerator.NoiseType.Fbm); + _drawNoiseGenerator.SetFrequency(0.5f); + + var resourceCache = IoCManager.Resolve(); + var font = resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13); + var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); + + MouseFilter = MouseFilterMode.Stop; + + _alarmControlAnimation = new Animation + { + Length = TimeSpan.FromSeconds(1), + AnimationTracks = + { + new AnimationTrackControlProperty + { + Property = nameof(Control.Visible), + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(true, 0), + new AnimationTrackProperty.KeyFrame(false, 0.75f), + } + } + } + }; + + var back = new StyleBoxTexture + { + Texture = panelTex, + Modulate = Color.FromHex("#25252A"), + }; + back.SetPatchMargin(StyleBox.Margin.All, 10); + + var back2 = new StyleBoxTexture(back) + { + Modulate = Color.FromHex("#202023") + }; + + AddChild(new PanelContainer + { + PanelOverride = back, + MouseFilter = MouseFilterMode.Pass + }); + + _stateSpinBox = new SpinBox + { + Value = 0, + }; + _stateSpinBox.IsValid = StrengthSpinBoxValid; + _stateSpinBox.InitDefaultButtons(); + _stateSpinBox.ValueChanged += PowerStateChanged; + _stateSpinBox.LineEditDisabled = true; + + _offButton = new Button + { + ToggleMode = false, + Text = "Off", + StyleClasses = {StyleBase.ButtonOpenRight}, + }; + _offButton.OnPressed += args => owner.SendEnableMessage(false); + + _onButton = new Button + { + ToggleMode = false, + Text = "On", + StyleClasses = {StyleBase.ButtonOpenLeft}, + }; + _onButton.OnPressed += args => owner.SendEnableMessage(true); + + var closeButton = new TextureButton + { + StyleClasses = {"windowCloseButton"}, + SizeFlagsHorizontal = SizeFlags.ShrinkEnd + }; + closeButton.OnPressed += args => Close(); + + var serviceManual = new Label + { + SizeFlagsHorizontal = SizeFlags.ShrinkCenter, + StyleClasses = {StyleBase.StyleClassLabelSubText}, + Text = Loc.GetString("Refer to p.132 of service manual") + }; + _drawLabel = new Label(); + var imgSize = new Vector2(32, 32); + AddChild(new VBoxContainer + { + Children = + { + new MarginContainer + { + MarginLeftOverride = 2, + MarginTopOverride = 2, + Children = + { + new Label + { + Text = Loc.GetString("Mark 2 Particle Accelerator"), + FontOverride = font, + FontColorOverride = StyleNano.NanoGold, + }, + new MarginContainer + { + MarginRightOverride = 8, + Children = + { + closeButton + } + } + } + }, + new PanelContainer + { + PanelOverride = new StyleBoxFlat {BackgroundColor = StyleNano.NanoGold}, + CustomMinimumSize = (0, 2), + }, + new Control + { + CustomMinimumSize = (0, 4) + }, + + new HBoxContainer + { + SizeFlagsVertical = SizeFlags.FillExpand, + Children = + { + new MarginContainer + { + MarginLeftOverride = 4, + Children = + { + new VBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + Children = + { + new HBoxContainer + { + Children = + { + new Label + { + Text = Loc.GetString("Power: "), + SizeFlagsHorizontal = SizeFlags.Expand + }, + _offButton, + _onButton + } + }, + new HBoxContainer + { + Children = + { + new Label + { + Text = Loc.GetString("Strength: "), + SizeFlagsHorizontal = SizeFlags.Expand + }, + _stateSpinBox + } + }, + new Control + { + CustomMinimumSize = (0, 10), + }, + _drawLabel, + new Control + { + SizeFlagsVertical = SizeFlags.Expand + }, + (_alarmControl = new VBoxContainer + { + Children = + { + new Label + { + Text = Loc.GetString("PARTICLE STRENGTH\nLIMITER FAILURE"), + FontColorOverride = Color.Red, + Align = Label.AlignMode.Center + }, + serviceManual + } + }), + } + } + } + }, + new VBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + Children = + { + (_statusLabel = new Label + { + SizeFlagsHorizontal = SizeFlags.ShrinkCenter + }), + new Control + { + CustomMinimumSize = (0, 20) + }, + new PanelContainer + { + SizeFlagsHorizontal = SizeFlags.ShrinkCenter, + PanelOverride = back2, + Children = + { + new GridContainer + { + Columns = 3, + VSeparationOverride = 0, + HSeparationOverride = 0, + Children = + { + new Control {CustomMinimumSize = imgSize}, + (_endCapTexture = Segment("end_cap")), + new Control {CustomMinimumSize = imgSize}, + (_controlBoxTexture = Segment("control_box")), + (_fuelChamberTexture = Segment("fuel_chamber")), + new Control {CustomMinimumSize = imgSize}, + new Control {CustomMinimumSize = imgSize}, + (_powerBoxTexture = Segment("power_box")), + new Control {CustomMinimumSize = imgSize}, + (_emitterLeftTexture = Segment("emitter_left")), + (_emitterCenterTexture = Segment("emitter_center")), + (_emitterRightTexture = Segment("emitter_right")), + } + } + } + }, + (_scanButton = new Button + { + Text = Loc.GetString("Scan Parts"), + SizeFlagsHorizontal = SizeFlags.ShrinkCenter + }) + } + } + } + }, + new StripeBack + { + Children = + { + new MarginContainer + { + MarginLeftOverride = 4, + MarginTopOverride = 4, + MarginBottomOverride = 4, + Children = + { + new Label + { + Text = Loc.GetString("Ensure containment field is active before operation"), + SizeFlagsHorizontal = SizeFlags.ShrinkCenter, + StyleClasses = {StyleBase.StyleClassLabelSubText}, + } + } + } + } + }, + new MarginContainer + { + MarginLeftOverride = 12, + Children = + { + new HBoxContainer + { + Children = + { + new Label + { + Text = "FOO-BAR-BAZ", + StyleClasses = {StyleBase.StyleClassLabelSubText} + } + } + } + } + }, + } + }); + + _scanButton.OnPressed += args => Owner.SendScanPartsMessage(); + + _alarmControl.AnimationCompleted += s => + { + if (_shouldContinueAnimating) + { + _alarmControl.PlayAnimation(_alarmControlAnimation, "warningAnim"); + } + else + { + _alarmControl.Visible = false; + } + }; + + PASegmentControl Segment(string name) + { + return new PASegmentControl(this, resourceCache, name); + } + } + + private bool StrengthSpinBoxValid(int n) + { + return (n >= 0 && n <= 4 && !_blockSpinBox); + } + + private void PowerStateChanged(object sender, ValueChangedEventArgs e) + { + ParticleAcceleratorPowerState newState; + switch (e.Value) + { + case 0: + newState = ParticleAcceleratorPowerState.Standby; + break; + case 1: + newState = ParticleAcceleratorPowerState.Level0; + break; + case 2: + newState = ParticleAcceleratorPowerState.Level1; + break; + case 3: + newState = ParticleAcceleratorPowerState.Level2; + break; + case 4: + newState = ParticleAcceleratorPowerState.Level3; + break; + default: + return; + } + + Owner.SendPowerStateMessage(newState); + } + + protected override DragMode GetDragModeFor(Vector2 relativeMousePos) + { + return DragMode.Move; + } + + protected override Vector2 CalculateMinimumSize() + { + return (400, 300); + } + + public void DataUpdate(ParticleAcceleratorUIState uiState) + { + _assembled = uiState.Assembled; + UpdateUI(uiState.Assembled, uiState.InterfaceBlock, uiState.Enabled, + uiState.WirePowerBlock); + _statusLabel.Text = Loc.GetString($"Status: {(uiState.Assembled ? "Operational" : "Incomplete")}"); + UpdatePowerState(uiState.State, uiState.Enabled, uiState.Assembled, + uiState.MaxLevel); + UpdatePreview(uiState); + _lastDraw = uiState.PowerDraw; + _lastReceive = uiState.PowerReceive; + } + + private void UpdatePowerState(ParticleAcceleratorPowerState state, bool enabled, bool assembled, + ParticleAcceleratorPowerState maxState) + { + _stateSpinBox.OverrideValue(state switch + { + ParticleAcceleratorPowerState.Standby => 0, + ParticleAcceleratorPowerState.Level0 => 1, + ParticleAcceleratorPowerState.Level1 => 2, + ParticleAcceleratorPowerState.Level2 => 3, + ParticleAcceleratorPowerState.Level3 => 4, + _ => 0 + }); + + + _shouldContinueAnimating = false; + _alarmControl.StopAnimation("warningAnim"); + _alarmControl.Visible = false; + if (maxState == ParticleAcceleratorPowerState.Level3 && enabled == true && assembled == true) + { + _shouldContinueAnimating = true; + _alarmControl.PlayAnimation(_alarmControlAnimation, "warningAnim"); + } + } + + private void UpdateUI(bool assembled, bool blocked, bool enabled, bool powerBlock) + { + _onButton.Pressed = enabled; + _offButton.Pressed = !enabled; + + var cantUse = !assembled || blocked || powerBlock; + _onButton.Disabled = cantUse; + _offButton.Disabled = cantUse; + _scanButton.Disabled = blocked; + + var cantChangeLevel = !assembled || blocked; + _stateSpinBox.SetButtonDisabled(cantChangeLevel); + _blockSpinBox = cantChangeLevel; + } + + private void UpdatePreview(ParticleAcceleratorUIState updateMessage) + { + _endCapTexture.SetPowerState(updateMessage, updateMessage.EndCapExists); + _fuelChamberTexture.SetPowerState(updateMessage, updateMessage.FuelChamberExists); + _controlBoxTexture.SetPowerState(updateMessage, true); + _powerBoxTexture.SetPowerState(updateMessage, updateMessage.PowerBoxExists); + _emitterCenterTexture.SetPowerState(updateMessage, updateMessage.EmitterCenterExists); + _emitterLeftTexture.SetPowerState(updateMessage, updateMessage.EmitterLeftExists); + _emitterRightTexture.SetPowerState(updateMessage, updateMessage.EmitterRightExists); + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + + if (!_assembled) + { + _drawLabel.Text = Loc.GetString("Draw: N/A"); + return; + } + + _time += args.DeltaSeconds; + + var watts = 0; + if (_lastDraw != 0) + { + var val = _drawNoiseGenerator.GetNoise(_time); + watts = (int) (_lastDraw + val * 5); + } + + _drawLabel.Text = Loc.GetString("Draw: {0:##,##0}/{1:##,##0} W", watts, _lastReceive); + } + + private sealed class PASegmentControl : Control + { + private readonly ParticleAcceleratorControlMenu _menu; + private readonly string _baseState; + private readonly TextureRect _base; + private readonly TextureRect _unlit; + private readonly RSI _rsi; + + public PASegmentControl(ParticleAcceleratorControlMenu menu, IResourceCache cache, string name) + { + _menu = menu; + _baseState = name; + _rsi = cache.GetResource($"/Textures/Constructible/Power/PA/{name}.rsi").RSI; + + AddChild(_base = new TextureRect {Texture = _rsi[$"{name}c"].Frame0}); + AddChild(_unlit = new TextureRect()); + } + + public void SetPowerState(ParticleAcceleratorUIState state, bool exists) + { + _base.ShaderOverride = exists ? null : _menu._greyScaleShader; + _base.ModulateSelfOverride = exists ? (Color?)null : new Color(127, 127, 127); + + if (!state.Enabled || !exists) + { + _unlit.Visible = false; + return; + } + + _unlit.Visible = true; + + var suffix = state.State switch + { + ParticleAcceleratorPowerState.Standby => "_unlitp", + ParticleAcceleratorPowerState.Level0 => "_unlitp0", + ParticleAcceleratorPowerState.Level1 => "_unlitp1", + ParticleAcceleratorPowerState.Level2 => "_unlitp2", + ParticleAcceleratorPowerState.Level3 => "_unlitp3", + _ => "" + }; + + if (!_rsi.TryGetState(_baseState + suffix, out var rState)) + { + _unlit.Visible = false; + return; + } + + _unlit.Texture = rState.Frame0; + } + + protected override Vector2 CalculateMinimumSize() + { + return _rsi.Size; + } + } + } +} diff --git a/Content.Server/Atmos/HighPressureMovementController.cs b/Content.Server/Atmos/HighPressureMovementController.cs index 4b494495e5..b112e4619e 100644 --- a/Content.Server/Atmos/HighPressureMovementController.cs +++ b/Content.Server/Atmos/HighPressureMovementController.cs @@ -2,6 +2,7 @@ using System; using Content.Server.GameObjects.Components.Atmos; using Content.Shared.Atmos; +using Content.Shared.Physics; using Robust.Shared.GameObjects.Components; using Robust.Shared.Interfaces.Physics; using Robust.Shared.Interfaces.Random; @@ -13,7 +14,7 @@ using Robust.Shared.Random; namespace Content.Server.Atmos { - public class HighPressureMovementController : VirtualController + public class HighPressureMovementController : FrictionController { [Dependency] private IRobustRandom _robustRandom = default!; [Dependency] private IPhysicsManager _physicsManager = default!; @@ -69,17 +70,5 @@ namespace Content.Server.Atmos } } } - - public override void UpdateAfterProcessing() - { - base.UpdateAfterProcessing(); - - if (ControlledComponent != null && !_physicsManager.IsWeightless(ControlledComponent.Owner.Transform.Coordinates)) - { - LinearVelocity *= 0.85f; - if (MathF.Abs(LinearVelocity.Length) < 1f) - Stop(); - } - } } } diff --git a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs index 27102390cf..0a8737e993 100644 --- a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs @@ -541,7 +541,7 @@ namespace Content.Server.GameObjects.Components.GUI var list = new List>(); foreach (var (slot, container) in _slotContainers) { - if (container.ContainedEntity != null) + if (container != null && container.ContainedEntity != null) { list.Add(new KeyValuePair(slot, container.ContainedEntity.Uid)); } diff --git a/Content.Server/GameObjects/Components/PA/ParticleAcceleratorControlBoxComponent.cs b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorControlBoxComponent.cs new file mode 100644 index 0000000000..a2f5c122d3 --- /dev/null +++ b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorControlBoxComponent.cs @@ -0,0 +1,724 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using Content.Server.GameObjects.Components.Power.ApcNetComponents; +using Content.Server.GameObjects.Components.Power.PowerNetComponents; +using Content.Server.GameObjects.Components.VendingMachines; +using Content.Server.Utility; +using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.Components.UserInterface; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Localization; +using Robust.Shared.Log; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; +using static Content.Shared.GameObjects.Components.SharedWiresComponent; +using Timer = Robust.Shared.Timers.Timer; + +namespace Content.Server.GameObjects.Components.PA +{ + // 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... + /// + [ComponentReference(typeof(IActivate))] + [RegisterComponent] + public class ParticleAcceleratorControlBoxComponent : ParticleAcceleratorPartComponent, IActivate, IWires + { + public override string Name => "ParticleAcceleratorControlBox"; + + [ViewVariables] + private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ParticleAcceleratorControlBoxUiKey.Key); + + /// + /// Power receiver for the control console itself. + /// + [ViewVariables] private PowerReceiverComponent? _powerReceiverComponent; + + [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. + /// + [ViewVariables(VVAccess.ReadWrite)] private TimeSpan _firingDelay; + + [ViewVariables(VVAccess.ReadWrite)] private int _powerDrawBase; + [ViewVariables(VVAccess.ReadWrite)] private int _powerDrawMult; + + [ViewVariables] private bool ConsolePowered => _powerReceiverComponent?.Powered ?? true; + + public ParticleAcceleratorControlBoxComponent() + { + Master = this; + } + + private ParticleAcceleratorPowerState MaxPower => _wireLimiterCut + ? ParticleAcceleratorPowerState.Level3 + : ParticleAcceleratorPowerState.Level2; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + // 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. + serializer.DataField(ref _firingDelay, "fireDelay", TimeSpan.FromSeconds(6)); + serializer.DataField(ref _powerDrawBase, "powerDrawBase", 500); + serializer.DataField(ref _powerDrawMult, "powerDrawMult", 1500); + } + + public override void Initialize() + { + base.Initialize(); + if (UserInterface != null) + { + UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage; + } + + if (!Owner.TryGetComponent(out _powerReceiverComponent)) + { + Logger.Error("ParticleAcceleratorControlBox was created without PowerReceiverComponent"); + return; + } + + _powerReceiverComponent.OnPowerStateChanged += OnPowerStateChanged; + _powerReceiverComponent.Load = 250; + } + + protected override void Startup() + { + base.Startup(); + + UpdateWireStatus(); + } + + // 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 OnPowerStateChanged(object? sender, PowerStateEventArgs e) + { + UpdateAppearance(); + + if (!e.Powered) + { + UserInterface?.CloseAll(); + } + } + + private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage obj) + { + if (!ConsolePowered) + { + return; + } + + + if (obj.Session.AttachedEntity == null || + !ActionBlockerSystem.CanInteract(obj.Session.AttachedEntity)) + { + return; + } + + if (_wireInterfaceBlocked) + { + return; + } + + switch (obj.Message) + { + case ParticleAcceleratorSetEnableMessage enableMessage: + if (enableMessage.Enabled) + { + SwitchOn(); + } + else + { + SwitchOff(); + } + + break; + + case ParticleAcceleratorSetPowerStateMessage stateMessage: + SetStrength(stateMessage.State); + break; + + case ParticleAcceleratorRescanPartsMessage _: + RescanParts(); + break; + } + + UpdateUI(); + } + + public void UpdateUI() + { + var draw = 0; + var receive = 0; + + if (_isEnabled) + { + draw = _partPowerBox!.PowerConsumerComponent!.DrawRate; + receive = _partPowerBox!.PowerConsumerComponent!.ReceivedPower; + } + + var state = new ParticleAcceleratorUIState( + _isAssembled, + _isEnabled, + _selectedStrength, + draw, + receive, + _partEmitterLeft != null, + _partEmitterCenter != null, + _partEmitterRight != null, + _partPowerBox != null, + _partFuelChamber != null, + _partEndCap != null, + _wireInterfaceBlocked, + MaxPower, + _wirePowerBlocked); + + UserInterface?.SetState(state); + } + + void IActivate.Activate(ActivateEventArgs eventArgs) + { + if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) + { + return; + } + + if (Owner.TryGetComponent(out var wires) && wires.IsPanelOpen) + { + wires.OpenInterface(actor.playerSession); + } + else + { + if (!ConsolePowered) + { + return; + } + + UserInterface?.Toggle(actor.playerSession); + UpdateUI(); + } + } + + public override void OnRemove() + { + UserInterface?.CloseAll(); + 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("The control box makes a whirring noise.")); + } + 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 (!Owner.TryGetComponent(out WiresComponent? wires)) + { + return; + } + + var powerBlock = _wirePowerBlocked; + var keyboardLight = new StatusLightData(Color.Green, + _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() + { + SwitchOff(); + foreach (var part in AllParts()) + { + part.Master = null; + } + + _isAssembled = false; + _partFuelChamber = null; + _partEndCap = null; + _partPowerBox = null; + _partEmitterLeft = null; + _partEmitterCenter = null; + _partEmitterRight = null; + + // Find fuel chamber first by scanning cardinals. + if (SnapGrid != null) + { + foreach (var maybeFuel in SnapGrid.GetCardinalNeighborCells()) + { + if (maybeFuel.Owner.TryGetComponent(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. + Owner.Transform.LocalRotation = _partFuelChamber.Owner.Transform.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()) + { + part.Master = this; + } + + UpdateUI(); + + Vector2i RotateOffset(in Vector2i vec) + { + var rot = new Angle(Owner.Transform.LocalRotation + Math.PI / 2); + return (Vector2i) rot.RotateVec(vec); + } + } + + private bool ScanPart(Vector2i offset, [NotNullWhen(true)] out T? part) + where T : ParticleAcceleratorPartComponent + { + foreach (var ent in SnapGrid!.GetOffset(offset)) + { + if (ent.TryGetComponent(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; + } + + private void SwitchOn() + { + DebugTools.Assert(_isAssembled); + + if (_isEnabled) + { + return; + } + + _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); + } + + private void SwitchOff() + { + _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(); + } + + private void SetStrength(ParticleAcceleratorPowerState state) + { + if (_wireStrengthCut) + { + return; + } + + state = (ParticleAcceleratorPowerState) MathHelper.Clamp( + (int) state, + (int) ParticleAcceleratorPowerState.Standby, + (int) MaxPower); + + _selectedStrength = state; + UpdateAppearance(); + UpdatePartVisualStates(); + + if (_isEnabled) + { + UpdatePowerDraw(); + UpdateFiring(); + } + } + + private void UpdateAppearance() + { + if (Owner.TryGetComponent(out AppearanceComponent? appearance)) + { + appearance.SetData(ParticleAcceleratorVisuals.VisualState, + _powerReceiverComponent!.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(object? sender, ReceivedPowerChangedEventArgs 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(ParticleAcceleratorPartComponent? component) + { + if (component == null || !component.Owner.TryGetComponent(out var appearanceComponent)) + { + return; + } + + var state = _isPowered + ? (ParticleAcceleratorVisualState) _selectedStrength + : ParticleAcceleratorVisualState.Unpowered; + appearanceComponent.SetData(ParticleAcceleratorVisuals.VisualState, state); + } + + public override void Rotated() + { + // We rotate OURSELVES when scanning for parts, so don't actually run rescan on rotate. + // That would be silly. + } + + 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 + } + } +} diff --git a/Content.Server/GameObjects/Components/PA/ParticleAcceleratorEmitterComponent.cs b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorEmitterComponent.cs new file mode 100644 index 0000000000..dd7643d3ff --- /dev/null +++ b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorEmitterComponent.cs @@ -0,0 +1,50 @@ +using Content.Shared.GameObjects.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.PA +{ + [RegisterComponent] + [ComponentReference(typeof(ParticleAcceleratorPartComponent))] + public class ParticleAcceleratorEmitterComponent : ParticleAcceleratorPartComponent + { + [Dependency] private IEntityManager _entityManager = null!; + + public override string Name => "ParticleAcceleratorEmitter"; + public ParticleAcceleratorEmitterType Type; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref Type, "emitterType", ParticleAcceleratorEmitterType.Center); + } + + public void Fire(ParticleAcceleratorPowerState strength) + { + var projectile = _entityManager.SpawnEntity("ParticlesProjectile", Owner.Transform.Coordinates); + + if (!projectile.TryGetComponent(out var particleProjectileComponent)) + { + Logger.Error("ParticleAcceleratorEmitter tried firing particles, but they was spawned without a ParticleProjectileComponent"); + return; + } + particleProjectileComponent.Fire(strength, Owner.Transform.WorldRotation, Owner); + } + + public override string ToString() + { + return base.ToString() + $" EmitterType:{Type}"; + } + } + + public enum ParticleAcceleratorEmitterType + { + Left, + Center, + Right + } +} diff --git a/Content.Server/GameObjects/Components/PA/ParticleAcceleratorEndCapComponent.cs b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorEndCapComponent.cs new file mode 100644 index 0000000000..e62bbde893 --- /dev/null +++ b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorEndCapComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameObjects; + +namespace Content.Server.GameObjects.Components.PA +{ + [RegisterComponent] + [ComponentReference(typeof(ParticleAcceleratorPartComponent))] + public class ParticleAcceleratorEndCapComponent : ParticleAcceleratorPartComponent + { + public override string Name => "ParticleAcceleratorEndCap"; + } +} diff --git a/Content.Server/GameObjects/Components/PA/ParticleAcceleratorFuelChamberComponent.cs b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorFuelChamberComponent.cs new file mode 100644 index 0000000000..ee3e18a348 --- /dev/null +++ b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorFuelChamberComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameObjects; + +namespace Content.Server.GameObjects.Components.PA +{ + [RegisterComponent] + [ComponentReference(typeof(ParticleAcceleratorPartComponent))] + public class ParticleAcceleratorFuelChamberComponent : ParticleAcceleratorPartComponent + { + public override string Name => "ParticleAcceleratorFuelChamber"; + } +} diff --git a/Content.Server/GameObjects/Components/PA/ParticleAcceleratorPartComponent.cs b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorPartComponent.cs new file mode 100644 index 0000000000..3f078cd71f --- /dev/null +++ b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorPartComponent.cs @@ -0,0 +1,58 @@ +#nullable enable +using Content.Server.Utility; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.GameObjects.Components.Transform; +using Robust.Shared.Log; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.PA +{ + public abstract class ParticleAcceleratorPartComponent : Component + { + [ViewVariables] private PhysicsComponent? _collidableComponent; + [ViewVariables] public ParticleAcceleratorControlBoxComponent? Master; + [ViewVariables] protected SnapGridComponent? SnapGrid; + + public override void Initialize() + { + base.Initialize(); + // FIXME: this has to be an entity system, full stop. + if (!Owner.TryGetComponent(out _collidableComponent)) + { + Logger.Error("ParticleAcceleratorPartComponent created with no CollidableComponent"); + } + else + { + _collidableComponent.AnchoredChanged += OnAnchorChanged; + } + + if (!Owner.TryGetComponent(out SnapGrid)) + { + Logger.Error("ParticleAcceleratorControlBox was created without SnapGridComponent"); + } + } + + public void OnAnchorChanged() + { + RescanIfPossible(); + } + + public override void OnRemove() + { + base.OnRemove(); + + RescanIfPossible(); + } + + private void RescanIfPossible() + { + Master?.RescanParts(); + } + + public virtual void Rotated() + { + RescanIfPossible(); + } + } +} diff --git a/Content.Server/GameObjects/Components/PA/ParticleAcceleratorPowerBoxComponent.cs b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorPowerBoxComponent.cs new file mode 100644 index 0000000000..4736c34a03 --- /dev/null +++ b/Content.Server/GameObjects/Components/PA/ParticleAcceleratorPowerBoxComponent.cs @@ -0,0 +1,33 @@ +#nullable enable +using Content.Server.GameObjects.Components.Power.PowerNetComponents; +using Robust.Shared.GameObjects; +using Robust.Shared.Log; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.PA +{ + [RegisterComponent] + [ComponentReference(typeof(ParticleAcceleratorPartComponent))] + public class ParticleAcceleratorPowerBoxComponent : ParticleAcceleratorPartComponent + { + public override string Name => "ParticleAcceleratorPowerBox"; + [ViewVariables] public PowerConsumerComponent? PowerConsumerComponent; + + public override void Initialize() + { + base.Initialize(); + if (Owner.TryGetComponent(out PowerConsumerComponent)) + { + PowerConsumerComponent.OnReceivedPowerChanged += PowerReceivedChanged; + return; + } + + Logger.Error($"ParticleAcceleratorPowerBoxComponent Component initialized without PowerConsumerComponent."); + } + + private void PowerReceivedChanged(object? sender, ReceivedPowerChangedEventArgs e) + { + Master?.PowerBoxReceivedChanged(sender, e); + } + } +} diff --git a/Content.Server/GameObjects/Components/PA/ParticleProjectileComponent.cs b/Content.Server/GameObjects/Components/PA/ParticleProjectileComponent.cs new file mode 100644 index 0000000000..5f24db47f7 --- /dev/null +++ b/Content.Server/GameObjects/Components/PA/ParticleProjectileComponent.cs @@ -0,0 +1,94 @@ +using System; +using Content.Server.GameObjects.Components.Projectiles; +using Content.Server.GameObjects.Components.Singularity; +using Content.Shared.GameObjects.Components; +using Content.Shared.Physics; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Log; +using Robust.Shared.Maths; +using Robust.Shared.Timers; + +namespace Content.Server.GameObjects.Components.PA +{ + [RegisterComponent] + public class ParticleProjectileComponent : Component, ICollideBehavior + { + public override string Name => "ParticleProjectile"; + private ParticleAcceleratorPowerState _state; + public void CollideWith(IEntity collidedWith) + { + if (collidedWith.TryGetComponent(out var singularityComponent)) + { + var multiplier = _state switch + { + ParticleAcceleratorPowerState.Standby => 0, + ParticleAcceleratorPowerState.Level0 => 1, + ParticleAcceleratorPowerState.Level1 => 3, + ParticleAcceleratorPowerState.Level2 => 6, + ParticleAcceleratorPowerState.Level3 => 10, + _ => 0 + }; + singularityComponent.Energy += 10 * multiplier; + Owner.Delete(); + }else if (collidedWith.TryGetComponent(out var singularityGeneratorComponent) + ) + { + singularityGeneratorComponent.Power += _state switch + { + ParticleAcceleratorPowerState.Standby => 0, + ParticleAcceleratorPowerState.Level0 => 1, + ParticleAcceleratorPowerState.Level1 => 2, + ParticleAcceleratorPowerState.Level2 => 4, + ParticleAcceleratorPowerState.Level3 => 8, + _ => 0 + }; + Owner.Delete(); + } + } + + public void Fire(ParticleAcceleratorPowerState state, Angle angle, IEntity firer) + { + _state = state; + + if (!Owner.TryGetComponent(out var physicsComponent)) + { + Logger.Error("ParticleProjectile tried firing, but it was spawned without a CollidableComponent"); + return; + } + physicsComponent.Status = BodyStatus.InAir; + + if (!Owner.TryGetComponent(out var projectileComponent)) + { + Logger.Error("ParticleProjectile tried firing, but it was spawned without a ProjectileComponent"); + return; + } + projectileComponent.IgnoreEntity(firer); + + var suffix = state switch + { + ParticleAcceleratorPowerState.Level0 => "0", + ParticleAcceleratorPowerState.Level1 => "1", + ParticleAcceleratorPowerState.Level2 => "2", + ParticleAcceleratorPowerState.Level3 => "3", + _ => "0" + }; + + if (!Owner.TryGetComponent(out var spriteComponent)) + { + Logger.Error("ParticleProjectile tried firing, but it was spawned without a SpriteComponent"); + return; + } + spriteComponent.LayerSetState(0, $"particle{suffix}"); + + physicsComponent + .EnsureController() + .LinearVelocity = angle.ToVec() * 20f; + + Owner.Transform.LocalRotation = new Angle(angle + Angle.FromDegrees(180)); + Timer.Spawn(3000, () => Owner.Delete()); + } + } +} diff --git a/Content.Server/GameObjects/Components/Power/PowerNetComponents/PowerConsumerComponent.cs b/Content.Server/GameObjects/Components/Power/PowerNetComponents/PowerConsumerComponent.cs index 28e37ad789..54dd3437d2 100644 --- a/Content.Server/GameObjects/Components/Power/PowerNetComponents/PowerConsumerComponent.cs +++ b/Content.Server/GameObjects/Components/Power/PowerNetComponents/PowerConsumerComponent.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using System; +using System.Diagnostics; using Content.Server.GameObjects.Components.NodeContainer.NodeGroups; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; @@ -33,6 +34,8 @@ namespace Content.Server.GameObjects.Components.Power.PowerNetComponents public int ReceivedPower { get => _receivedPower; set => SetReceivedPower(value); } private int _receivedPower; + public event EventHandler OnReceivedPowerChanged; + public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); @@ -60,7 +63,9 @@ namespace Content.Server.GameObjects.Components.Power.PowerNetComponents private void SetReceivedPower(int newReceivedPower) { Debug.Assert(newReceivedPower >= 0 && newReceivedPower <= DrawRate); + if(_receivedPower == newReceivedPower) return; _receivedPower = newReceivedPower; + OnReceivedPowerChanged?.Invoke(this, new ReceivedPowerChangedEventArgs(_drawRate, _receivedPower)); } private void SetPriority(Priority newPriority) @@ -75,4 +80,16 @@ namespace Content.Server.GameObjects.Components.Power.PowerNetComponents First, Last, } + + public class ReceivedPowerChangedEventArgs : EventArgs + { + public readonly int DrawRate; + public readonly int ReceivedPower; + + public ReceivedPowerChangedEventArgs(int drawRate, int receivedPower) + { + DrawRate = drawRate; + ReceivedPower = receivedPower; + } + } } diff --git a/Content.Server/GameObjects/Components/Power/PowerNetComponents/RadiationCollectorComponent.cs b/Content.Server/GameObjects/Components/Power/PowerNetComponents/RadiationCollectorComponent.cs new file mode 100644 index 0000000000..51d378b5d0 --- /dev/null +++ b/Content.Server/GameObjects/Components/Power/PowerNetComponents/RadiationCollectorComponent.cs @@ -0,0 +1,97 @@ +using System; +using System.Threading; +using Content.Server.Utility; +using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Doors; +using Content.Shared.GameObjects.Components.Singularity; +using Content.Shared.Interfaces; +using Content.Shared.Interfaces.GameObjects.Components; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Interfaces.Timing; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Log; +using Timer = Robust.Shared.Timers.Timer; + +namespace Content.Server.GameObjects.Components.Power.PowerNetComponents +{ + [RegisterComponent] + public class RadiationCollectorComponent : PowerSupplierComponent, IInteractHand, IRadiationAct + { + [Dependency] private readonly IGameTiming _gameTiming = default!; + + public override string Name => "RadiationCollector"; + private bool _enabled; + private TimeSpan _coolDownEnd; + + private PhysicsComponent _collidableComponent; + + public override void Initialize() + { + base.Initialize(); + if (!Owner.TryGetComponent(out _collidableComponent)) + { + Logger.Error("RadiationCollectorComponent created with no CollidableComponent"); + return; + } + _collidableComponent.AnchoredChanged += OnAnchoredChanged; + } + + private void OnAnchoredChanged() + { + if(_collidableComponent.Anchored) Owner.SnapToGrid(); + } + + bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs) + { + var curTime = _gameTiming.CurTime; + + if(curTime < _coolDownEnd) + return true; + + if (!_enabled) + { + Owner.PopupMessage(eventArgs.User, Loc.GetString("The collector turns on.")); + EnableCollection(); + } + else + { + Owner.PopupMessage(eventArgs.User, Loc.GetString("The collector turns off.")); + DisableCollection(); + } + + _coolDownEnd = curTime + TimeSpan.FromSeconds(0.81f); + + return true; + } + + void EnableCollection() + { + _enabled = true; + SetAppearance(RadiationCollectorVisualState.Activating); + } + + void DisableCollection() + { + _enabled = false; + SetAppearance(RadiationCollectorVisualState.Deactivating); + } + + public void RadiationAct(float frameTime, SharedRadiationPulseComponent radiation) + { + if (!_enabled) return; + + SupplyRate = (int) (frameTime * radiation.RadsPerSecond * 3000f); + } + + protected void SetAppearance(RadiationCollectorVisualState state) + { + if (Owner.TryGetComponent(out AppearanceComponent appearance)) + { + appearance.SetData(RadiationCollectorVisuals.VisualState, state); + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Projectiles/EmitterBoltComponent.cs b/Content.Server/GameObjects/Components/Projectiles/EmitterBoltComponent.cs new file mode 100644 index 0000000000..83f489818a --- /dev/null +++ b/Content.Server/GameObjects/Components/Projectiles/EmitterBoltComponent.cs @@ -0,0 +1,13 @@ +using Content.Server.GameObjects.Components.Singularity; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; + +namespace Content.Server.GameObjects.Components.Projectiles +{ + [RegisterComponent] + public class EmitterBoltComponent : Component + { + public override string Name => "EmitterBoltComponent"; + } +} diff --git a/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs b/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs index f4751d35f9..3c0669ba18 100644 --- a/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs +++ b/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs @@ -60,6 +60,8 @@ namespace Content.Server.GameObjects.Components.Projectiles Dirty(); } + private bool _internalDeleteOnCollide; + /// /// Applies the damage when our projectile collides with its victim /// @@ -74,12 +76,12 @@ namespace Content.Server.GameObjects.Components.Projectiles // This is so entities that shouldn't get a collision are ignored. if (entity.TryGetComponent(out IPhysicsComponent otherPhysics) && otherPhysics.Hard == false) { - _deleteOnCollide = false; + _internalDeleteOnCollide = false; return; } else { - _deleteOnCollide = true; + _internalDeleteOnCollide = true; } if (_soundHitSpecies != null && entity.HasComponent()) @@ -112,7 +114,7 @@ namespace Content.Server.GameObjects.Components.Projectiles void ICollideBehavior.PostCollide(int collideCount) { - if (collideCount > 0 && DeleteOnCollide) Owner.Delete(); + if (collideCount > 0 && DeleteOnCollide && _internalDeleteOnCollide) Owner.Delete(); } public override ComponentState GetComponentState() diff --git a/Content.Server/GameObjects/Components/Singularity/ContainmentFieldComponent.cs b/Content.Server/GameObjects/Components/Singularity/ContainmentFieldComponent.cs new file mode 100644 index 0000000000..2a85124e49 --- /dev/null +++ b/Content.Server/GameObjects/Components/Singularity/ContainmentFieldComponent.cs @@ -0,0 +1,29 @@ +#nullable enable +using System; +using Content.Shared.Physics; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Maths; +using Robust.Shared.Physics; + +namespace Content.Server.GameObjects.Components.Singularity +{ + [RegisterComponent] + public class ContainmentFieldComponent : Component, ICollideBehavior + { + public override string Name => "ContainmentField"; + public ContainmentFieldConnection? Parent; + + public void CollideWith(IEntity collidedWith) + { + if (Parent == null) + { + Owner.Delete(); + return; + } + + Parent.TryRepell(Owner, collidedWith); + } + } +} diff --git a/Content.Server/GameObjects/Components/Singularity/ContainmentFieldConnection.cs b/Content.Server/GameObjects/Components/Singularity/ContainmentFieldConnection.cs new file mode 100644 index 0000000000..15b5b88d37 --- /dev/null +++ b/Content.Server/GameObjects/Components/Singularity/ContainmentFieldConnection.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using Content.Shared.Physics; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Maths; +using Timer = Robust.Shared.Timers.Timer; + +namespace Content.Server.GameObjects.Components.Singularity +{ + public class ContainmentFieldConnection : IDisposable + { + public readonly ContainmentFieldGeneratorComponent Generator1; + public readonly ContainmentFieldGeneratorComponent Generator2; + private List _fields = new List(); + private int _sharedEnergyPool; + private CancellationTokenSource _powerDecreaseCancellationTokenSource = new CancellationTokenSource(); + public int SharedEnergyPool + { + get => _sharedEnergyPool; + set + { + _sharedEnergyPool = Math.Clamp(value, 0, 10); + if (_sharedEnergyPool == 0) + { + Dispose(); + } + } + } + + public ContainmentFieldConnection(ContainmentFieldGeneratorComponent generator1, ContainmentFieldGeneratorComponent generator2) + { + Generator1 = generator1; + Generator2 = generator2; + + //generateFields + var pos1 = generator1.Owner.Transform.Coordinates; + var pos2 = generator2.Owner.Transform.Coordinates; + if (pos1 == pos2) + { + Dispose(); + return; + } + + var entityManager = IoCManager.Resolve(); + + var delta = (pos2 - pos1).Position; + var dirVec = delta.Normalized; + var stopDist = delta.Length; + var currentOffset = dirVec; + while (currentOffset.Length < stopDist) + { + var currentCoords = pos1.Offset(currentOffset); + var newEnt = entityManager.SpawnEntity("ContainmentField", currentCoords); + if (!newEnt.TryGetComponent(out var containmentFieldComponent)) + { + Logger.Error("While creating Fields in ContainmentFieldConnection, a ContainmentField without a ContainmentFieldComponent was created. Deleting newly spawned ContainmentField..."); + newEnt.Delete(); + continue; + } + + containmentFieldComponent.Parent = this; + newEnt.Transform.WorldRotation = dirVec.ToAngle(); + + _fields.Add(newEnt); + currentOffset += dirVec; + } + + + Timer.SpawnRepeating(1000, () => { SharedEnergyPool--;}, _powerDecreaseCancellationTokenSource.Token); + } + + public bool CanRepell(IEntity toRepell) + { + var powerNeeded = 1; + if (toRepell.TryGetComponent(out var singularityComponent)) + { + powerNeeded += 2*singularityComponent.Level; + } + + return _sharedEnergyPool > powerNeeded; + } + + /// + /// Tries to repell a Entity. This deletes the connection if the repelling fails! + /// + /// Entity to repell from. Should be a field, otherwise return will be false. + /// Entity to repell. + public void TryRepell(IEntity repellFrom, IEntity toRepell) + { + if (!_fields.Contains(repellFrom) || !toRepell.TryGetComponent(out var collidableComponent)) return; + + var speed = 5; + var containmentFieldRepellController = collidableComponent.EnsureController(); + + if (!CanRepell(toRepell)) + { + Dispose(); + return; + } + + if (Math.Abs(repellFrom.Transform.WorldRotation.Degrees + 90f) < 0.1f || + Math.Abs(repellFrom.Transform.WorldRotation.Degrees - 90f) < 0.1f) + { + if (repellFrom.Transform.WorldPosition.X.CompareTo(toRepell.Transform.WorldPosition.X) > 0) + { + containmentFieldRepellController.Repell(Direction.West, speed); + } + else + { + containmentFieldRepellController.Repell(Direction.East, speed); + } + } + else + { + if (repellFrom.Transform.WorldPosition.Y.CompareTo(toRepell.Transform.WorldPosition.Y) > 0) + { + containmentFieldRepellController.Repell(Direction.South, speed); + } + else + { + containmentFieldRepellController.Repell(Direction.North, speed); + } + } + + return; + } + + public void Dispose() + { + _powerDecreaseCancellationTokenSource.Cancel(); + foreach (var field in _fields) + { + field.Delete(); + } + _fields.Clear(); + + Generator1.RemoveConnection(this); + Generator2.RemoveConnection(this); + } + } +} diff --git a/Content.Server/GameObjects/Components/Singularity/ContainmentFieldGeneratorComponent.cs b/Content.Server/GameObjects/Components/Singularity/ContainmentFieldGeneratorComponent.cs new file mode 100644 index 0000000000..92bf31aac9 --- /dev/null +++ b/Content.Server/GameObjects/Components/Singularity/ContainmentFieldGeneratorComponent.cs @@ -0,0 +1,206 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Server.GameObjects.Components.Projectiles; +using Content.Server.Utility; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Physics; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Physics; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Log; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Physics; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Singularity +{ + [RegisterComponent] + public class ContainmentFieldGeneratorComponent : Component, ICollideBehavior + { + [Dependency] private IPhysicsManager _physicsManager = null!; + [Dependency] private IEntityManager _entityManager = null!; + + public override string Name => "ContainmentFieldGenerator"; + + private int _powerBuffer; + + [ViewVariables] + public int PowerBuffer + { + get => _powerBuffer; + set => _powerBuffer = Math.Clamp(value, 0, 6); + } + + public void ReceivePower(int power) + { + var totalPower = power + PowerBuffer; + var powerPerConnection = totalPower / 2; + var newBuffer = totalPower % 2; + TryPowerConnection(ref _connection1, ref newBuffer, powerPerConnection); + TryPowerConnection(ref _connection2, ref newBuffer, powerPerConnection); + + PowerBuffer = newBuffer; + } + + private void TryPowerConnection(ref Tuple? connectionProperty, ref int powerBuffer, int powerPerConnection) + { + if (connectionProperty != null) + { + connectionProperty.Item2.SharedEnergyPool += powerPerConnection; + } + else + { + if (TryGenerateFieldConnection(ref connectionProperty)) + { + connectionProperty.Item2.SharedEnergyPool += powerPerConnection; + } + else + { + powerBuffer += powerPerConnection; + } + } + } + + private PhysicsComponent? _collidableComponent; + + private Tuple? _connection1; + private Tuple? _connection2; + + public bool CanRepell(IEntity toRepell) => _connection1?.Item2?.CanRepell(toRepell) == true || + _connection2?.Item2?.CanRepell(toRepell) == true; + + public override void Initialize() + { + base.Initialize(); + if (!Owner.TryGetComponent(out _collidableComponent)) + { + Logger.Error("ContainmentFieldGeneratorComponent created with no CollidableComponent"); + return; + } + _collidableComponent.AnchoredChanged += OnAnchoredChanged; + } + + + private void OnAnchoredChanged() + { + if(_collidableComponent?.Anchored == true) + { + Owner.SnapToGrid(); + } + else + { + _connection1?.Item2.Dispose(); + _connection2?.Item2.Dispose(); + } + } + + private bool IsConnectedWith(ContainmentFieldGeneratorComponent comp) + { + + return comp == this || _connection1?.Item2.Generator1 == comp || _connection1?.Item2.Generator2 == comp || + _connection2?.Item2.Generator1 == comp || _connection2?.Item2.Generator2 == comp; + } + + public bool HasFreeConnections() + { + return _connection1 == null || _connection2 == null; + } + + private bool TryGenerateFieldConnection([NotNullWhen(true)] ref Tuple? propertyFieldTuple) + { + if (propertyFieldTuple != null) return false; + if(_collidableComponent?.Anchored == false) return false; + + foreach (var direction in new[] {Direction.North, Direction.East, Direction.South, Direction.West}) + { + if (_connection1?.Item1 == direction || _connection2?.Item1 == direction) continue; + + var dirVec = direction.ToVec(); + var ray = new CollisionRay(Owner.Transform.WorldPosition, dirVec, (int) CollisionGroup.MobMask); + var rawRayCastResults = _physicsManager.IntersectRay(Owner.Transform.MapID, ray, 4.5f, Owner, false); + + var rayCastResults = rawRayCastResults as RayCastResults[] ?? rawRayCastResults.ToArray(); + if(!rayCastResults.Any()) continue; + + RayCastResults? closestResult = null; + var smallestDist = 4.5f; + foreach (var res in rayCastResults) + { + if (res.Distance > smallestDist) continue; + + smallestDist = res.Distance; + closestResult = res; + } + if(closestResult == null) continue; + var ent = closestResult.Value.HitEntity; + if (!ent.TryGetComponent(out var fieldGeneratorComponent) || + fieldGeneratorComponent.Owner == Owner || + !fieldGeneratorComponent.HasFreeConnections() || + IsConnectedWith(fieldGeneratorComponent) || + !ent.TryGetComponent(out var collidableComponent) || + !collidableComponent.Anchored) + { + continue; + } + + var connection = new ContainmentFieldConnection(this, fieldGeneratorComponent); + propertyFieldTuple = new Tuple(direction, connection); + if (fieldGeneratorComponent._connection1 == null) + { + fieldGeneratorComponent._connection1 = new Tuple(direction.GetOpposite(), connection); + } + else if (fieldGeneratorComponent._connection2 == null) + { + fieldGeneratorComponent._connection2 = new Tuple(direction.GetOpposite(), connection); + } + else + { + Logger.Error("When trying to connect two Containmentfieldgenerators, the second one already had two connection but the check didn't catch it"); + } + + return true; + } + + return false; + } + + public void RemoveConnection(ContainmentFieldConnection? connection) + { + if (_connection1?.Item2 == connection) + { + _connection1 = null; + }else if (_connection2?.Item2 == connection) + { + _connection2 = null; + } + else if(connection != null) + { + Logger.Error("RemoveConnection called on Containmentfieldgenerator with a connection that can't be found in its connections."); + } + } + + public void CollideWith(IEntity collidedWith) + { + if(collidedWith.HasComponent()) + { + ReceivePower(4); + } + } + + public override void OnRemove() + { + _connection1?.Item2.Dispose(); + _connection2?.Item2.Dispose(); + base.OnRemove(); + } + } +} diff --git a/Content.Server/GameObjects/Components/Singularity/EmitterComponent.cs b/Content.Server/GameObjects/Components/Singularity/EmitterComponent.cs new file mode 100644 index 0000000000..75a4353ad1 --- /dev/null +++ b/Content.Server/GameObjects/Components/Singularity/EmitterComponent.cs @@ -0,0 +1,309 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Content.Server.GameObjects.Components.Access; +using Content.Server.GameObjects.Components.Power.PowerNetComponents; +using Content.Server.GameObjects.Components.Projectiles; +using Content.Server.Interfaces; +using Content.Server.Utility; +using Content.Shared.GameObjects.Components.Singularity; +using Content.Shared.Interfaces; +using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Physics; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.ComponentDependencies; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Log; +using Robust.Shared.Random; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; +using Timer = Robust.Shared.Timers.Timer; + +#nullable enable + +namespace Content.Server.GameObjects.Components.Singularity +{ + [RegisterComponent] + [ComponentReference(typeof(IActivate))] + public class EmitterComponent : Component, IActivate, IInteractUsing + { + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + + [ComponentDependency] private AppearanceComponent? _appearance; + [ComponentDependency] private AccessReader? _accessReader; + + public override string Name => "Emitter"; + + private CancellationTokenSource? _timerCancel; + + private PhysicsComponent _collidableComponent = default!; + private PowerConsumerComponent _powerConsumer = default!; + + // whether the power switch is in "on" + [ViewVariables] private bool _isOn; + // Whether the power switch is on AND the machine has enough power (so is actively firing) + [ViewVariables] private bool _isPowered; + [ViewVariables] private bool _isLocked; + + [ViewVariables(VVAccess.ReadWrite)] private int _fireShotCounter; + + [ViewVariables(VVAccess.ReadWrite)] private string _fireSound = default!; + [ViewVariables(VVAccess.ReadWrite)] private string _boltType = default!; + [ViewVariables(VVAccess.ReadWrite)] private int _powerUseActive; + [ViewVariables(VVAccess.ReadWrite)] private int _fireBurstSize; + [ViewVariables(VVAccess.ReadWrite)] private TimeSpan _fireInterval; + [ViewVariables(VVAccess.ReadWrite)] private TimeSpan _fireBurstDelayMin; + [ViewVariables(VVAccess.ReadWrite)] private TimeSpan _fireBurstDelayMax; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _fireBurstDelayMin, "fireBurstDelayMin", TimeSpan.FromSeconds(2)); + serializer.DataField(ref _fireBurstDelayMax, "fireBurstDelayMax", TimeSpan.FromSeconds(10)); + serializer.DataField(ref _fireInterval, "fireInterval", TimeSpan.FromSeconds(2)); + serializer.DataField(ref _fireBurstSize, "fireBurstSize", 3); + serializer.DataField(ref _powerUseActive, "powerUseActive", 500); + serializer.DataField(ref _boltType, "boltType", "EmitterBolt"); + serializer.DataField(ref _fireSound, "fireSound", "/Audio/Weapons/emitter.ogg"); + } + + public override void Initialize() + { + base.Initialize(); + + if (!Owner.TryGetComponent(out _collidableComponent!)) + { + Logger.Error($"EmitterComponent {Owner} created with no CollidableComponent"); + return; + } + + if (!Owner.TryGetComponent(out _powerConsumer!)) + { + Logger.Error($"EmitterComponent {Owner} created with no PowerConsumerComponent"); + return; + } + + _collidableComponent.AnchoredChanged += OnAnchoredChanged; + _powerConsumer.OnReceivedPowerChanged += OnReceivedPowerChanged; + } + + private void OnReceivedPowerChanged(object? sender, ReceivedPowerChangedEventArgs e) + { + if (!_isOn) + { + return; + } + + if (e.ReceivedPower < e.DrawRate) + { + PowerOff(); + } + else + { + PowerOn(); + } + } + + private void OnAnchoredChanged() + { + if (_collidableComponent.Anchored) + Owner.SnapToGrid(); + } + + void IActivate.Activate(ActivateEventArgs eventArgs) + { + if (_isLocked) + { + Owner.PopupMessage(eventArgs.User, Loc.GetString("{0:TheName} is access locked!", Owner)); + return; + } + + if (!_isOn) + { + SwitchOn(); + Owner.PopupMessage(eventArgs.User, Loc.GetString("{0:TheName} turns on.", Owner)); + } + else + { + SwitchOff(); + Owner.PopupMessage(eventArgs.User, Loc.GetString("{0:TheName} turns off.", Owner)); + } + } + + Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) + { + if (_accessReader == null || !eventArgs.Using.TryGetComponent(out IAccess? access)) + { + return Task.FromResult(false); + } + + if (_accessReader.IsAllowed(access)) + { + _isLocked ^= true; + + if (_isLocked) + { + Owner.PopupMessage(eventArgs.User, Loc.GetString("You lock {0:TheName}.", Owner)); + } + else + { + Owner.PopupMessage(eventArgs.User, Loc.GetString("You unlock {0:TheName}.", Owner)); + } + + UpdateAppearance(); + } + else + { + Owner.PopupMessage(eventArgs.User, Loc.GetString("Access denied.")); + } + + return Task.FromResult(true); + } + + private void SwitchOff() + { + _isOn = false; + _powerConsumer.DrawRate = 0; + PowerOff(); + UpdateAppearance(); + } + + private void SwitchOn() + { + _isOn = true; + _powerConsumer.DrawRate = _powerUseActive; + // Do not directly PowerOn(). + // OnReceivedPowerChanged will get fired due to DrawRate change which will turn it on. + UpdateAppearance(); + } + + private void PowerOff() + { + if (!_isPowered) + { + return; + } + + _isPowered = false; + + // Must be set while emitter powered. + DebugTools.AssertNotNull(_timerCancel); + _timerCancel!.Cancel(); + + UpdateAppearance(); + } + + private void PowerOn() + { + if (_isPowered) + { + return; + } + + _isPowered = true; + + _fireShotCounter = 0; + _timerCancel = new CancellationTokenSource(); + + Timer.Spawn(_fireBurstDelayMax, ShotTimerCallback, _timerCancel.Token); + + UpdateAppearance(); + } + + private void ShotTimerCallback() + { + // Any power-off condition should result in the timer for this method being cancelled + // and thus not firing + DebugTools.Assert(_isPowered); + DebugTools.Assert(_isOn); + DebugTools.Assert(_powerConsumer.DrawRate <= _powerConsumer.ReceivedPower); + + Fire(); + + TimeSpan delay; + if (_fireShotCounter < _fireBurstSize) + { + _fireShotCounter += 1; + delay = _fireInterval; + } + else + { + _fireShotCounter = 0; + var diff = _fireBurstDelayMax - _fireBurstDelayMin; + // TIL you can do TimeSpan * double. + delay = _fireBurstDelayMin + _robustRandom.NextFloat() * diff; + } + + // Must be set while emitter powered. + DebugTools.AssertNotNull(_timerCancel); + Timer.Spawn(delay, ShotTimerCallback, _timerCancel!.Token); + } + + private void Fire() + { + var projectile = _entityManager.SpawnEntity(_boltType, Owner.Transform.Coordinates); + + if (!projectile.TryGetComponent(out var physicsComponent)) + { + Logger.Error("Emitter tried firing a bolt, but it was spawned without a CollidableComponent"); + return; + } + + physicsComponent.Status = BodyStatus.InAir; + + if (!projectile.TryGetComponent(out var projectileComponent)) + { + Logger.Error("Emitter tried firing a bolt, but it was spawned without a ProjectileComponent"); + return; + } + + projectileComponent.IgnoreEntity(Owner); + + physicsComponent + .EnsureController() + .LinearVelocity = Owner.Transform.WorldRotation.ToVec() * 20f; + + projectile.Transform.LocalRotation = Owner.Transform.WorldRotation; + + // TODO: Move to projectile's code. + Timer.Spawn(3000, () => projectile.Delete()); + + EntitySystem.Get().PlayFromEntity(_fireSound, Owner); + } + + private void UpdateAppearance() + { + if (_appearance == null) + { + return; + } + + EmitterVisualState state; + if (_isPowered) + { + state = EmitterVisualState.On; + } + else if (_isOn) + { + state = EmitterVisualState.Underpowered; + } + else + { + state = EmitterVisualState.Off; + } + + _appearance.SetData(EmitterVisuals.VisualState, state); + _appearance.SetData(EmitterVisuals.Locked, _isLocked); + } + } +} diff --git a/Content.Server/GameObjects/Components/Singularity/SingularityComponent.cs b/Content.Server/GameObjects/Components/Singularity/SingularityComponent.cs new file mode 100644 index 0000000000..10ffcb51fe --- /dev/null +++ b/Content.Server/GameObjects/Components/Singularity/SingularityComponent.cs @@ -0,0 +1,227 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Linq; +using Content.Server.GameObjects.Components.StationEvents; +using Content.Shared.GameObjects; +using Content.Shared.GameObjects.EntitySystemMessages; +using Content.Shared.Physics; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.Audio; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.GameObjects.Components.Map; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Maths; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Timers; + +namespace Content.Server.GameObjects.Components.Singularity +{ + [RegisterComponent] + public class SingularityComponent : Component, ICollideBehavior + { + [Dependency] private IEntityManager _entityManager = null!; + [Dependency] private IMapManager _mapManager = null!; + [Dependency] private IRobustRandom _random = null!; + + + public override uint? NetID => ContentNetIDs.SINGULARITY; + + public override string Name => "Singularity"; + + public int Energy + { + get => _energy; + set + { + if (value == _energy) return; + + _energy = value; + if (_energy <= 0) + { + if(_singularityController != null) _singularityController.LinearVelocity = Vector2.Zero; + _spriteComponent?.LayerSetVisible(0, false); + + Owner.Delete(); + return; + } + + Level = _energy switch + { + var n when n >= 1500 => 6, + var n when n >= 1000 => 5, + var n when n >= 600 => 4, + var n when n >= 300 => 3, + var n when n >= 200 => 2, + var n when n < 200 => 1, + _ => 1 + }; + } + } + private int _energy = 180; + + public int Level + { + get => _level; + set + { + if (value == _level) return; + if (value < 0) value = 0; + if (value > 6) value = 6; + + _level = value; + + if(_radiationPulseComponent != null) _radiationPulseComponent.RadsPerSecond = 10 * value; + + _spriteComponent?.LayerSetRSI(0, "Effects/Singularity/singularity_" + _level + ".rsi"); + _spriteComponent?.LayerSetState(0, "singularity_" + _level); + + if(_collidableComponent != null && _collidableComponent.PhysicsShapes.Any() && _collidableComponent.PhysicsShapes[0] is PhysShapeCircle circle) + { + circle.Radius = _level - 0.5f; + } + } + } + private int _level; + + public int EnergyDrain => + Level switch + { + 6 => 20, + 5 => 15, + 4 => 10, + 3 => 5, + 2 => 2, + 1 => 1, + _ => 0 + }; + + private SingularityController? _singularityController; + private PhysicsComponent? _collidableComponent; + private SpriteComponent? _spriteComponent; + private RadiationPulseComponent? _radiationPulseComponent; + private AudioSystem _audioSystem = null!; + private AudioSystem.AudioSourceServer? _playingSound; + + public override void Initialize() + { + base.Initialize(); + + _audioSystem = EntitySystem.Get(); + var audioParams = AudioParams.Default; + audioParams.Loop = true; + audioParams.MaxDistance = 20f; + audioParams.Volume = 5; + _audioSystem.PlayFromEntity("/Audio/Effects/singularity_form.ogg", Owner); + Timer.Spawn(5200,() => _playingSound = _audioSystem.PlayFromEntity("/Audio/Effects/singularity.ogg", Owner, audioParams)); + + + if (!Owner.TryGetComponent(out _collidableComponent)) + { + Logger.Error("SingularityComponent was spawned without CollidableComponent"); + } + else + { + _collidableComponent.Hard = false; + } + + if (!Owner.TryGetComponent(out _spriteComponent)) + { + Logger.Error("SingularityComponent was spawned without SpriteComponent"); + } + + _singularityController = _collidableComponent?.EnsureController(); + if(_singularityController!=null)_singularityController.ControlledComponent = _collidableComponent; + + if (!Owner.TryGetComponent(out _radiationPulseComponent)) + { + Logger.Error("SingularityComponent was spawned without RadiationPulseComponent"); + } + + Level = 1; + } + + public void Update() + { + Energy -= EnergyDrain; + + if(Level == 1) return; + //pushing + var pushVector = new Vector2((_random.Next(-10, 10)), _random.Next(-10, 10)); + while (pushVector.X == 0 && pushVector.Y == 0) + { + pushVector = new Vector2((_random.Next(-10, 10)), _random.Next(-10, 10)); + } + _singularityController?.Push(pushVector.Normalized, 2); + } + + List _previousPulledEntites = new List(); + public void PullUpdate() + { + foreach (var previousPulledEntity in _previousPulledEntites) + { + if(previousPulledEntity.Deleted) continue; + if (!previousPulledEntity.TryGetComponent(out var collidableComponent)) continue; + var controller = collidableComponent.EnsureController(); + controller.StopPull(); + } + _previousPulledEntites.Clear(); + + var entitiesToPull = _entityManager.GetEntitiesInRange(Owner.Transform.Coordinates, Level * 10); + foreach (var entity in entitiesToPull) + { + if (!entity.TryGetComponent(out var collidableComponent)) continue; + var controller = collidableComponent.EnsureController(); + if(Owner.Transform.Coordinates.EntityId != entity.Transform.Coordinates.EntityId) continue; + var vec = (Owner.Transform.Coordinates - entity.Transform.Coordinates).Position; + if (vec == Vector2.Zero) continue; + + var speed = 10 / vec.Length * Level; + + controller.Pull(vec.Normalized, speed); + _previousPulledEntites.Add(entity); + } + } + + void ICollideBehavior.CollideWith(IEntity entity) + { + if (_collidableComponent == null) return; //how did it even collide then? :D + + if (entity.TryGetComponent(out var mapGridComponent)) + { + foreach (var tile in mapGridComponent.Grid.GetTilesIntersecting(((IPhysBody) _collidableComponent).WorldAABB)) + { + mapGridComponent.Grid.SetTile(tile.GridIndices, Tile.Empty); + Energy++; + } + return; + } + + if (entity.HasComponent() || (entity.TryGetComponent(out var component) && component.CanRepell(Owner))) + { + return; + } + + if (ContainerHelpers.IsInContainer(entity)) return; + + entity.Delete(); + Energy++; + } + + public override void OnRemove() + { + _playingSound?.Stop(); + _audioSystem.PlayAtCoords("/Audio/Effects/singularity_collapse.ogg", Owner.Transform.Coordinates); + base.OnRemove(); + } + } +} diff --git a/Content.Server/GameObjects/Components/Singularity/SingularityGenerator.cs b/Content.Server/GameObjects/Components/Singularity/SingularityGenerator.cs new file mode 100644 index 0000000000..7cb11986d9 --- /dev/null +++ b/Content.Server/GameObjects/Components/Singularity/SingularityGenerator.cs @@ -0,0 +1,31 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Server.GameObjects.Components.Singularity +{ + [RegisterComponent] + public class SingularityGeneratorComponent : Component + { + public override string Name => "SingularityGenerator"; + + private int _power; + + public int Power + { + get => _power; + set + { + if(_power == value) return; + + _power = value; + if (_power > 15) + { + var entityManager = IoCManager.Resolve(); + entityManager.SpawnEntity("Singularity", Owner.Transform.Coordinates); + //dont delete ourselves, just wait to get eaten + } + } + } + } +} diff --git a/Content.Server/GameObjects/Components/WiresComponent.cs b/Content.Server/GameObjects/Components/WiresComponent.cs index 100157f0a7..cb2f201faa 100644 --- a/Content.Server/GameObjects/Components/WiresComponent.cs +++ b/Content.Server/GameObjects/Components/WiresComponent.cs @@ -373,6 +373,14 @@ namespace Content.Server.GameObjects.Components UserInterface?.Open(session); } + /// + /// Closes all wire UIs. + /// + public void CloseAll() + { + UserInterface?.CloseAll(); + } + private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg) { var message = serverMsg.Message; diff --git a/Content.Server/GameObjects/EntitySystems/ParticleAcceleratorPartSystem.cs b/Content.Server/GameObjects/EntitySystems/ParticleAcceleratorPartSystem.cs new file mode 100644 index 0000000000..114028e9b1 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/ParticleAcceleratorPartSystem.cs @@ -0,0 +1,28 @@ +#nullable enable +using Content.Server.GameObjects.Components.PA; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components.Transform; +using Robust.Shared.GameObjects.Systems; + +namespace Content.Server.GameObjects.EntitySystems +{ + [UsedImplicitly] + public class ParticleAcceleratorPartSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + EntityManager.EventBus.SubscribeEvent(EventSource.Local, this, RotateEvent); + } + + private static void RotateEvent(RotateEvent ev) + { + if (ev.Sender.TryGetComponent(out ParticleAcceleratorPartComponent? part)) + { + part.Rotated(); + } + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/SingularitySystem.cs b/Content.Server/GameObjects/EntitySystems/SingularitySystem.cs new file mode 100644 index 0000000000..825f8a61e1 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/SingularitySystem.cs @@ -0,0 +1,41 @@ +using Content.Server.GameObjects.Components.Singularity; +using Robust.Shared.GameObjects.Systems; + +namespace Content.Server.GameObjects.EntitySystems +{ + public class SingularitySystem : EntitySystem + { + private float curTimeSingulo; + private float curTimePull; + public override void Update(float frameTime) + { + base.Update(frameTime); + + curTimeSingulo += frameTime; + curTimePull += frameTime; + + var shouldUpdate = curTimeSingulo >= 1f; + var shouldPull = curTimePull >= 0.2f; + if (!shouldUpdate && !shouldPull) return; + var singulos = ComponentManager.EntityQuery(); + + if (curTimeSingulo >= 1f) + { + curTimeSingulo -= 1f; + foreach (var singulo in singulos) + { + singulo.Update(); + } + } + + if (curTimePull >= 0.5f) + { + curTimePull -= 0.5f; + foreach (var singulo in singulos) + { + singulo.PullUpdate(); + } + } + } + } +} diff --git a/Content.Shared/GameObjects/Components/Singularity/SharedEmitterComponent.cs b/Content.Shared/GameObjects/Components/Singularity/SharedEmitterComponent.cs new file mode 100644 index 0000000000..7844f3f215 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Singularity/SharedEmitterComponent.cs @@ -0,0 +1,20 @@ +using System; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Singularity +{ + [NetSerializable, Serializable] + public enum EmitterVisuals + { + VisualState, + Locked + } + + [NetSerializable, Serializable] + public enum EmitterVisualState + { + On, + Underpowered, + Off + } +} diff --git a/Content.Shared/GameObjects/Components/Singularity/SharedParticleAcceleratorComponent.cs b/Content.Shared/GameObjects/Components/Singularity/SharedParticleAcceleratorComponent.cs new file mode 100644 index 0000000000..bdf5919356 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Singularity/SharedParticleAcceleratorComponent.cs @@ -0,0 +1,125 @@ +using System; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components +{ + [NetSerializable, Serializable] + public enum ParticleAcceleratorVisuals + { + VisualState + } + + [NetSerializable, Serializable] + public enum ParticleAcceleratorVisualState + { + //Open, //no prefix + //Wired, //w prefix + Unpowered, //c prefix + Powered, //p prefix + Level0, //0 prefix + Level1, //1 prefix + Level2, //2 prefix + Level3 //3 prefix + } + + [NetSerializable, Serializable] + public enum ParticleAcceleratorPowerState + { + Standby = ParticleAcceleratorVisualState.Powered, + Level0 = ParticleAcceleratorVisualState.Level0, + Level1 = ParticleAcceleratorVisualState.Level1, + Level2 = ParticleAcceleratorVisualState.Level2, + Level3 = ParticleAcceleratorVisualState.Level3 + } + + public enum ParticleAcceleratorVisualLayers + { + Base, + Unlit + } + + [Serializable, NetSerializable] + public enum ParticleAcceleratorWireStatus + { + Power, + Keyboard, + Limiter, + Strength, + } + + [NetSerializable, Serializable] + public class ParticleAcceleratorUIState : BoundUserInterfaceState + { + public bool Assembled; + public bool Enabled; + public ParticleAcceleratorPowerState State; + public int PowerDraw; + 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 PowerBoxExists; + public bool FuelChamberExists; + public bool EndCapExists; + + public bool InterfaceBlock; + 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) + { + Assembled = assembled; + Enabled = enabled; + State = state; + PowerDraw = powerDraw; + PowerReceive = powerReceive; + EmitterLeftExists = emitterLeftExists; + EmitterCenterExists = emitterCenterExists; + EmitterRightExists = emitterRightExists; + PowerBoxExists = powerBoxExists; + FuelChamberExists = fuelChamberExists; + EndCapExists = endCapExists; + InterfaceBlock = interfaceBlock; + MaxLevel = maxLevel; + WirePowerBlock = wirePowerBlock; + } + } + + [NetSerializable, Serializable] + public class ParticleAcceleratorSetEnableMessage : BoundUserInterfaceMessage + { + public readonly bool Enabled; + public ParticleAcceleratorSetEnableMessage(bool enabled) + { + Enabled = enabled; + } + } + + [NetSerializable, Serializable] + public class ParticleAcceleratorRescanPartsMessage : BoundUserInterfaceMessage + { + public ParticleAcceleratorRescanPartsMessage() + { + } + } + + [NetSerializable, Serializable] + public class ParticleAcceleratorSetPowerStateMessage : BoundUserInterfaceMessage + { + public readonly ParticleAcceleratorPowerState State; + + public ParticleAcceleratorSetPowerStateMessage(ParticleAcceleratorPowerState state) + { + State = state; + } + } + + [NetSerializable, Serializable] + public enum ParticleAcceleratorControlBoxUiKey + { + Key + } +} diff --git a/Content.Shared/GameObjects/Components/Singularity/SharedRadiationCollectorComponent.cs b/Content.Shared/GameObjects/Components/Singularity/SharedRadiationCollectorComponent.cs new file mode 100644 index 0000000000..f2bc67424f --- /dev/null +++ b/Content.Shared/GameObjects/Components/Singularity/SharedRadiationCollectorComponent.cs @@ -0,0 +1,20 @@ +using System; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Singularity +{ + [NetSerializable, Serializable] + public enum RadiationCollectorVisuals + { + VisualState + } + + [NetSerializable, Serializable] + public enum RadiationCollectorVisualState + { + Active, + Activating, + Deactivating, + Deactive + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index 32ebc3ed06..8769dd85d4 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -83,7 +83,7 @@ public const uint STORABLE = 1077; public const uint PULLABLE = 1078; public const uint GAS_TANK = 1079; - + public const uint SINGULARITY = 1080; // Net IDs for integration tests. public const uint PREDICTION_TEST = 10001; diff --git a/Content.Shared/GameObjects/EntitySystemMessages/SingularitySoundMessage.cs b/Content.Shared/GameObjects/EntitySystemMessages/SingularitySoundMessage.cs new file mode 100644 index 0000000000..66392f6c43 --- /dev/null +++ b/Content.Shared/GameObjects/EntitySystemMessages/SingularitySoundMessage.cs @@ -0,0 +1,20 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.EntitySystemMessages +{ + [Serializable, NetSerializable] + public class SingularitySoundMessage : ComponentMessage + { + public bool Start { get; } + + public SingularitySoundMessage(bool start) + { + Directed = true; + Start = start; + } + } + + +} diff --git a/Content.Shared/Physics/CollisionGroup.cs b/Content.Shared/Physics/CollisionGroup.cs index 0834d69787..c5d2448076 100644 --- a/Content.Shared/Physics/CollisionGroup.cs +++ b/Content.Shared/Physics/CollisionGroup.cs @@ -21,7 +21,8 @@ namespace Content.Shared.Physics SmallImpassable = 1 << 4, // 16 Things a smaller object - a cat, a crab - can't go through - a wall, but not a computer terminal or a table Clickable = 1 << 5, // 32 Temporary "dummy" layer to ensure that objects can still be clicked even if they don't collide with anything (you can't interact with objects that have no layer, including items) GhostImpassable = 1 << 6, // 64 Things impassible by ghosts/observers, ie blessed tiles or forcefields - + Underplating = 1 << 7, // 128 Things that are under plating + Passable = 1 << 8, // 256 Things that are passable MapGrid = MapGridHelpers.CollisionGroup, // Map grids, like shuttles. This is the actual grid itself, not the walls or other entities connected to the grid. MobMask = Impassable | MobImpassable | VaultImpassable | SmallImpassable, diff --git a/Content.Shared/Physics/ContainmentFieldCollisionController.cs b/Content.Shared/Physics/ContainmentFieldCollisionController.cs new file mode 100644 index 0000000000..c2e56646d2 --- /dev/null +++ b/Content.Shared/Physics/ContainmentFieldCollisionController.cs @@ -0,0 +1,9 @@ +using Robust.Shared.Physics; + +namespace Content.Shared.Physics +{ + public class ContainmentFieldCollisionController : VirtualController + { + + } +} diff --git a/Content.Shared/Physics/ContainmentFieldRepellController.cs b/Content.Shared/Physics/ContainmentFieldRepellController.cs new file mode 100644 index 0000000000..314dbc811a --- /dev/null +++ b/Content.Shared/Physics/ContainmentFieldRepellController.cs @@ -0,0 +1,14 @@ +using System.Numerics; +using Robust.Shared.Maths; +using Robust.Shared.Physics; + +namespace Content.Shared.Physics +{ + public class ContainmentFieldRepellController : FrictionController + { + public void Repell(Direction dir, float speed) + { + LinearVelocity = dir.ToVec() * speed; + } + } +} diff --git a/Content.Shared/Physics/FrictionController.cs b/Content.Shared/Physics/FrictionController.cs new file mode 100644 index 0000000000..92e9e17baf --- /dev/null +++ b/Content.Shared/Physics/FrictionController.cs @@ -0,0 +1,24 @@ +using System; +using Robust.Shared.Interfaces.Physics; +using Robust.Shared.IoC; +using Robust.Shared.Physics; + +namespace Content.Shared.Physics +{ + public abstract class FrictionController : VirtualController + { + [Dependency] private IPhysicsManager _physicsManager = default!; + + public override void UpdateAfterProcessing() + { + base.UpdateAfterProcessing(); + + if (ControlledComponent != null && !_physicsManager.IsWeightless(ControlledComponent.Owner.Transform.Coordinates)) + { + LinearVelocity *= 0.85f; + if (MathF.Abs(LinearVelocity.Length) < 1f) + Stop(); + } + } + } +} diff --git a/Content.Shared/Physics/SingularityController.cs b/Content.Shared/Physics/SingularityController.cs new file mode 100644 index 0000000000..2aafe8d5a6 --- /dev/null +++ b/Content.Shared/Physics/SingularityController.cs @@ -0,0 +1,18 @@ +#nullable enable +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Maths; +using Robust.Shared.Physics; + +namespace Content.Shared.Physics +{ + public class SingularityController : VirtualController + { + public override IPhysicsComponent? ControlledComponent { protected get; set; } + + + public void Push(Vector2 velocityDirection, float speed) + { + LinearVelocity = velocityDirection * speed; + } + } +} diff --git a/Content.Shared/Physics/SingularityPullController.cs b/Content.Shared/Physics/SingularityPullController.cs new file mode 100644 index 0000000000..0a235fe1d0 --- /dev/null +++ b/Content.Shared/Physics/SingularityPullController.cs @@ -0,0 +1,22 @@ +#nullable enable +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Maths; +using Robust.Shared.Physics; + +namespace Content.Server.GameObjects.Components.Singularity +{ + public class SingularityPullController : VirtualController + { + public override IPhysicsComponent? ControlledComponent { protected get; set; } + + public void StopPull() + { + LinearVelocity = Vector2.Zero; + } + + public void Pull(Vector2 velocityDirection, float speed) + { + LinearVelocity = velocityDirection * speed; + } + } +} diff --git a/Resources/Audio/Effects/singularity.ogg b/Resources/Audio/Effects/singularity.ogg new file mode 100644 index 0000000000..c426e1ff4c Binary files /dev/null and b/Resources/Audio/Effects/singularity.ogg differ diff --git a/Resources/Audio/Effects/singularity_collapse.ogg b/Resources/Audio/Effects/singularity_collapse.ogg new file mode 100644 index 0000000000..8ec6adf947 Binary files /dev/null and b/Resources/Audio/Effects/singularity_collapse.ogg differ diff --git a/Resources/Audio/Effects/singularity_form.ogg b/Resources/Audio/Effects/singularity_form.ogg new file mode 100644 index 0000000000..0d53d0d665 Binary files /dev/null and b/Resources/Audio/Effects/singularity_form.ogg differ diff --git a/Resources/Audio/Weapons/emitter.ogg b/Resources/Audio/Weapons/emitter.ogg new file mode 100644 index 0000000000..46e0153dc9 Binary files /dev/null and b/Resources/Audio/Weapons/emitter.ogg differ diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/beds.yml b/Resources/Prototypes/Entities/Constructible/Furniture/beds.yml index b313528a1a..23cdc76fd0 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/beds.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/beds.yml @@ -10,7 +10,7 @@ anchored: true shapes: - !type:PhysShapeAabb - bounds: "-0.5, -0.5, 0.3, 0.5" + bounds: "-0.45, -0.45, 0.05, 0.45" mask: - Impassable - MobImpassable diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/instruments.yml b/Resources/Prototypes/Entities/Constructible/Furniture/instruments.yml index 3d2a01c6e6..6b6855427a 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/instruments.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/instruments.yml @@ -11,6 +11,7 @@ - type: Physics shapes: - !type:PhysShapeAabb + layer: [MobMask] mask: - Impassable - MobImpassable diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/potted_plants.yml b/Resources/Prototypes/Entities/Constructible/Furniture/potted_plants.yml index 1efad5174f..a5bcf67edd 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/potted_plants.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/potted_plants.yml @@ -55,6 +55,11 @@ components: - type: Sprite state: plant-25 + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.47,-0.25,0.05,0.25" + layer: [ Passable ] - type: entity id: PottedPlantBioluminscent diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/seats.yml b/Resources/Prototypes/Entities/Constructible/Furniture/seats.yml index 9f98b49d36..746bb2ae2c 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/seats.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/seats.yml @@ -27,6 +27,11 @@ - type: Sprite state: chair color: "#8e9799" + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.45, -0.45, 0.05, 0.45" + layer: [ Passable ] - type: entity name: stool @@ -37,6 +42,11 @@ - type: Sprite state: stool_base color: "#8e9799" + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5, -0.25, 0.05, 0.25" + layer: [ Passable ] - type: entity name: bar stool @@ -46,6 +56,11 @@ - type: Sprite state: bar_stool color: "white" + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5, -0.2, 0.2, 0.25" + layer: [ Passable ] - type: entity name: white office chair @@ -55,11 +70,16 @@ - type: Rotatable - type: Sprite state: officechair_white + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.49, -0.25, 0.37, 0.25" + layer: [ Passable ] - type: entity name: dark office chair id: ChairOfficeDark - parent: SeatBase + parent: ChairOfficeLight components: - type: Sprite state: officechair_dark @@ -72,6 +92,11 @@ components: - type: Sprite state: comfychair_preview + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.45, -0.3, 0.35, 0.3" + layer: [ MobMask ] - type: entity name: wooden chair @@ -81,3 +106,8 @@ - type: Sprite state: wooden_chair color: "white" + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.37, -0.25, 0.49, 0.24" + layer: [ Passable ] diff --git a/Resources/Prototypes/Entities/Constructible/Ground/catwalk.yml b/Resources/Prototypes/Entities/Constructible/Ground/catwalk.yml index a046dbc29c..5f7993755c 100644 --- a/Resources/Prototypes/Entities/Constructible/Ground/catwalk.yml +++ b/Resources/Prototypes/Entities/Constructible/Ground/catwalk.yml @@ -6,6 +6,10 @@ - type: Clickable - type: InteractionOutline - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5,-0.5,0.5,0.5" + layer: [ Passable ] - type: Sprite netsync: false sprite: Constructible/Tiles/catwalk.rsi diff --git a/Resources/Prototypes/Entities/Constructible/Power/particleAccelerator.yml b/Resources/Prototypes/Entities/Constructible/Power/particleAccelerator.yml new file mode 100644 index 0000000000..19be0d019e --- /dev/null +++ b/Resources/Prototypes/Entities/Constructible/Power/particleAccelerator.yml @@ -0,0 +1,348 @@ +- type: entity + id: ParticleAcceleratorBase + abstract: true + placement: + mode: SnapgridCenter + components: + - type: InteractionOutline + - type: Anchorable + - type: Physics + mass: 25 + anchored: true + shapes: + - !type:PhysShapeAabb + bounds: "-0.49,-0.49,0.49,0.49" + layer: + - Opaque + - Impassable + - MobImpassable + - VaultImpassable + IsScrapingFloor: true + - type: SnapGrid + offset: Center + - type: Pullable + - type: Clickable + +- type: entity + name: Particles + description: "Accelerated particles." + id: ParticlesProjectile + parent: BulletBase + components: + - type: Sprite + sprite: Constructible/Power/PA/particle.rsi + state: particle0 + shader: unshaded + - type: Projectile + delete_on_collide: false + soundHit: /Audio/Weapons/Guns/Hits/bullet_hit.ogg + damages: + Radiation: 10 + - type: Physics + hard: false + shapes: + - !type:PhysShapeAabb + bounds: "-0.48,-0.48,0.48,0.48" + layer: [None] + mask: + - MobMask + - Clickable + - type: ParticleProjectile + +# Working PA + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorControlBox + name: Particle Accelerator Control Computer + description: This controls the density of the particles. + components: + - type: Sprite + sprite: Constructible/Power/PA/control_box.rsi + layers: + - state: control_boxc + map: [ "enum.ParticleAcceleratorVisualLayers.Base" ] + - state: control_box_unlitp + map: [ "enum.ParticleAcceleratorVisualLayers.Unlit" ] + shader: unshaded + visible: false + - type: Appearance + visuals: + - type: ParticleAcceleratorPartVisualizer + baseState: control_box_unlit + - type: PowerReceiver + - type: ParticleAcceleratorControlBox + - type: Construction + graph: particleAcceleratorControlBox + node: completed + - type: UserInterface + interfaces: + - key: enum.ParticleAcceleratorControlBoxUiKey.Key + type: ParticleAcceleratorBoundUserInterface + - key: enum.WiresUiKey.Key + type: WiresBoundUserInterface + - type: Wires + BoardName: "Mk2 Particle Accelerator" + LayoutId: ParticleAccelerator + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorEmitterLeft + name: EM Containment Grid + suffix: Left + description: This launchs the Alpha particles, might not want to stand near this end. + components: + - type: Sprite + sprite: Constructible/Power/PA/emitter_left.rsi + layers: + - state: emitter_leftc + map: [ "enum.ParticleAcceleratorVisualLayers.Base" ] + - state: emitter_left_unlitp + map: [ "enum.ParticleAcceleratorVisualLayers.Unlit" ] + shader: unshaded + visible: false + - type: Appearance + visuals: + - type: ParticleAcceleratorPartVisualizer + baseState: emitter_left_unlit + - type: ParticleAcceleratorEmitter + emitterType: Left + - type: Construction + graph: particleAcceleratorEmitterLeft + node: completed + + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorEmitterCenter + name: EM Containment Grid + suffix: Center + description: This launchs the Alpha particles, might not want to stand near this end. + components: + - type: Sprite + sprite: Constructible/Power/PA/emitter_center.rsi + layers: + - state: emitter_centerc + map: [ "enum.ParticleAcceleratorVisualLayers.Base" ] + - state: emitter_center_unlitp + map: [ "enum.ParticleAcceleratorVisualLayers.Unlit" ] + shader: unshaded + visible: false + - type: Appearance + visuals: + - type: ParticleAcceleratorPartVisualizer + baseState: emitter_center_unlit + - type: ParticleAcceleratorEmitter + emitterType: Center + - type: Construction + graph: particleAcceleratorEmitterCenter + node: completed + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorEmitterRight + name: EM Containment Grid + suffix: Right + description: This launchs the Alpha particles, might not want to stand near this end. + components: + - type: Sprite + sprite: Constructible/Power/PA/emitter_right.rsi + layers: + - state: emitter_rightc + map: [ "enum.ParticleAcceleratorVisualLayers.Base" ] + - state: emitter_right_unlitp + map: [ "enum.ParticleAcceleratorVisualLayers.Unlit" ] + shader: unshaded + visible: false + - type: Appearance + visuals: + - type: ParticleAcceleratorPartVisualizer + baseState: emitter_right_unlit + - type: ParticleAcceleratorEmitter + emitterType: Right + - type: Construction + graph: particleAcceleratorEmitterRight + node: completed + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorEndCap + name: Alpha Particle Generation Array + description: This is where Alpha particles are generated from [REDACTED]. + components: + - type: Sprite + sprite: Constructible/Power/PA/end_cap.rsi + state: end_capc + - type: ParticleAcceleratorEndCap + - type: Construction + graph: particleAcceleratorEndCap + node: completed + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorFuelChamber + name: EM Acceleration Chamber + description: This is where the Alpha particles are accelerated to radical speeds. + components: + - type: Sprite + sprite: Constructible/Power/PA/fuel_chamber.rsi + layers: + - state: fuel_chamberc + map: [ "enum.ParticleAcceleratorVisualLayers.Base" ] + - state: fuel_chamber_unlitp + map: [ "enum.ParticleAcceleratorVisualLayers.Unlit" ] + shader: unshaded + visible: false + - type: Appearance + visuals: + - type: ParticleAcceleratorPartVisualizer + baseState: fuel_chamber_unlit + - type: ParticleAcceleratorFuelChamber + - type: Construction + graph: particleAcceleratorFuelChamber + node: completed + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorPowerBox + name: Particle Focusing EM Lens + description: This uses electromagnetic waves to focus the Alpha-Particles. + components: + - type: Sprite + sprite: Constructible/Power/PA/power_box.rsi + layers: + - state: power_boxc + map: [ "enum.ParticleAcceleratorVisualLayers.Base" ] + - state: power_box_unlitp + map: [ "enum.ParticleAcceleratorVisualLayers.Unlit" ] + shader: unshaded + visible: false + - type: Appearance + visuals: + - type: ParticleAcceleratorPartVisualizer + baseState: power_box_unlit + - type: ParticleAcceleratorPowerBox + - type: PowerConsumer + voltage: High + - type: NodeContainer + nodes: + - !type:AdjacentNode + nodeGroupID: HVPower + - type: Construction + graph: particleAcceleratorPowerBox + node: completed + + +# Unfinished PA + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorControlBoxUnfinished + name: Particle Accelerator Control Computer + suffix: Unfinished + description: This controls the density of the particles. It looks unfinished. + components: + - type: Physics + anchored: false + - type: Sprite + sprite: Constructible/Power/PA/control_box.rsi + state: control_box + - type: Construction + graph: particleAcceleratorControlBox + node: start + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorEmitterLeftUnfinished + name: EM Containment Grid + suffix: Unfinished, Left + description: This launchs the Alpha particles, might not want to stand near this end. It looks unfinished. + components: + - type: Physics + anchored: false + - type: Sprite + sprite: Constructible/Power/PA/emitter_left.rsi + state: emitter_left + - type: Construction + graph: particleAcceleratorEmitterLeft + node: start + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorEmitterCenterUnfinished + name: EM Containment Grid + suffix: Unfinished, Center + description: This launchs the Alpha particles, might not want to stand near this end. It looks unfinished. + components: + - type: Physics + anchored: false + - type: Sprite + sprite: Constructible/Power/PA/emitter_center.rsi + state: emitter_center + - type: Construction + graph: particleAcceleratorEmitterCenter + node: start + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorEmitterRightUnfinished + name: EM Containment Grid + suffix: Unfinished, Right + description: This launchs the Alpha particles, might not want to stand near this end. It looks unfinished. + components: + - type: Physics + anchored: false + - type: Sprite + sprite: Constructible/Power/PA/emitter_right.rsi + state: emitter_right + - type: Construction + graph: particleAcceleratorEmitterRight + node: start + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorEndCapUnfinished + name: Alpha Particle Generation Array + suffix: Unfinished + description: This is where Alpha particles are generated from [REDACTED]. It looks unfinished. + components: + - type: Physics + anchored: false + - type: Sprite + sprite: Constructible/Power/PA/end_cap.rsi + state: end_cap + - type: Construction + graph: particleAcceleratorEndCap + node: start + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorFuelChamberUnfinished + name: EM Acceleration Chamber + suffix: Unfinished + description: This is where the Alpha particles are accelerated to radical speeds. It looks unfinished. + components: + - type: Physics + anchored: false + - type: Sprite + sprite: Constructible/Power/PA/fuel_chamber.rsi + state: fuel_chamber + - type: Construction + graph: particleAcceleratorFuelChamber + node: start + +- type: entity + parent: ParticleAcceleratorBase + id: ParticleAcceleratorPowerBoxUnfinished + name: Particle Focusing EM Lens + suffix: Unfinished + description: This uses electromagnetic waves to focus the Alpha-Particles. It looks unfinished. + components: + - type: Physics + anchored: false + - type: Sprite + sprite: Constructible/Power/PA/power_box.rsi + state: power_box + - type: Construction + graph: particleAcceleratorPowerBox + node: start diff --git a/Resources/Prototypes/Entities/Constructible/Power/power_base.yml b/Resources/Prototypes/Entities/Constructible/Power/power_base.yml index c712d4a1b8..a39c8d9568 100644 --- a/Resources/Prototypes/Entities/Constructible/Power/power_base.yml +++ b/Resources/Prototypes/Entities/Constructible/Power/power_base.yml @@ -153,8 +153,9 @@ - type: InteractionOutline - type: Physics shapes: - - !type:PhysShapeAabb - bounds: "-0.25, -0.25, 0.25, 0.3" + - !type:PhysShapeAabb + bounds: "-0.25, -0.25, 0.25, 0.3" + layer: [ Passable ] - type: SnapGrid offset: Center - type: Sprite diff --git a/Resources/Prototypes/Entities/Constructible/Power/research.yml b/Resources/Prototypes/Entities/Constructible/Power/research.yml index f83b5f4c2b..167807e7c9 100644 --- a/Resources/Prototypes/Entities/Constructible/Power/research.yml +++ b/Resources/Prototypes/Entities/Constructible/Power/research.yml @@ -13,6 +13,7 @@ anchored: true shapes: - !type:PhysShapeAabb + bounds: "-0.4, -0.45, 0.45, 0.45" mask: - Impassable - MobImpassable diff --git a/Resources/Prototypes/Entities/Constructible/Power/wires.yml b/Resources/Prototypes/Entities/Constructible/Power/wires.yml index 488267e981..4e3baf5e3e 100644 --- a/Resources/Prototypes/Entities/Constructible/Power/wires.yml +++ b/Resources/Prototypes/Entities/Constructible/Power/wires.yml @@ -6,8 +6,12 @@ mode: SnapgridCenter components: - type: Clickable - - type: InteractionOutline - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5, -0.5, 0.5, 0.5" + layer: [ Underplating ] + - type: InteractionOutline - type: SnapGrid offset: Center - type: Sprite diff --git a/Resources/Prototypes/Entities/Constructible/Specific/conveyor.yml b/Resources/Prototypes/Entities/Constructible/Specific/conveyor.yml index 392722ddca..af06c06b62 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/conveyor.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/conveyor.yml @@ -12,6 +12,7 @@ shapes: - !type:PhysShapeAabb bounds: "-0.49,-0.49,0.49,0.49" + layer: [Passable] mask: - Impassable - MobImpassable diff --git a/Resources/Prototypes/Entities/Constructible/Specific/disposal.yml b/Resources/Prototypes/Entities/Constructible/Specific/disposal.yml index 7bf50b731f..5391b72a0b 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/disposal.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/disposal.yml @@ -35,7 +35,6 @@ drawdepth: BelowFloor sprite: Constructible/Power/disposal.rsi state: conpipe-s - - type: DisposalTransit - type: Appearance visuals: @@ -43,6 +42,11 @@ state_free: conpipe-s state_anchored: pipe-s state_broken: pipe-b + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5,-0.25,0.5,0.25" + layer: [ Underplating ] - type: entity id: DisposalTagger @@ -54,7 +58,6 @@ drawdepth: BelowFloor sprite: Constructible/Power/disposal.rsi state: conpipe-tagger - - type: DisposalTagger - type: Appearance visuals: @@ -66,6 +69,11 @@ interfaces: - key: enum.DisposalTaggerUiKey.Key type: DisposalTaggerBoundUserInterface + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5,-0.25,0.5,0.25" + layer: [ Underplating ] - type: entity id: DisposalTrunk @@ -77,7 +85,6 @@ drawdepth: BelowFloor sprite: Constructible/Power/disposal.rsi state: conpipe-t - - type: DisposalEntry - type: Appearance visuals: @@ -85,6 +92,11 @@ state_free: conpipe-t state_anchored: pipe-t state_broken: pipe-b + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5,-0.25,0.4,0.25" + layer: [ Underplating ] - type: entity id: DisposalUnit @@ -105,7 +117,6 @@ map: ["enum.DisposalUnitVisualLayers.Handle"] - state: dispover-ready map: ["enum.DisposalUnitVisualLayers.Light"] - - type: PowerReceiver - type: DisposalUnit flushTime: 2 @@ -115,7 +126,7 @@ anchored: true shapes: - !type:PhysShapeAabb - bounds: "-0.35,-0.3,0.35,0.3" + bounds: "-0.3,-0.35,0.3,0.35" mask: - Impassable - MobImpassable @@ -162,7 +173,6 @@ drawdepth: BelowFloor sprite: Constructible/Power/disposal.rsi state: conpipe-j1s - - type: DisposalRouter degrees: - 0 @@ -180,17 +190,21 @@ interfaces: - key: enum.DisposalRouterUiKey.Key type: DisposalRouterBoundUserInterface + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5,-0.5,0.5,0.25" + layer: [ Underplating ] - type: entity id: DisposalRouterFlipped parent: DisposalRouter - name: flipped router junction + suffix: flipped components: - type: Sprite drawdepth: BelowFloor sprite: Constructible/Power/disposal.rsi state: conpipe-j2s - - type: DisposalRouter degrees: - 0 @@ -204,6 +218,11 @@ state_broken: pipe-b - type: Flippable entity: DisposalRouter + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5,-0.25,0.5,0.5" + layer: [ Underplating ] - type: entity id: DisposalJunction @@ -215,7 +234,6 @@ drawdepth: BelowFloor sprite: Constructible/Power/disposal.rsi state: conpipe-j1 - - type: DisposalJunction degrees: - 0 @@ -229,17 +247,21 @@ state_broken: pipe-b - type: Flippable entity: DisposalJunctionFlipped + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5,-0.5,0.5,0.25" + layer: [ Underplating ] - type: entity id: DisposalJunctionFlipped parent: DisposalJunction - name: flipped disposal junction + suffix: flipped components: - type: Sprite drawdepth: BelowFloor sprite: Constructible/Power/disposal.rsi state: conpipe-j2 - - type: DisposalJunction degrees: - 0 @@ -253,6 +275,11 @@ state_broken: pipe-b - type: Flippable entity: DisposalJunction + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5,-0.25,0.5,0.5" + layer: [ Underplating ] - type: entity id: DisposalYJunction @@ -264,7 +291,6 @@ drawdepth: BelowFloor sprite: Constructible/Power/disposal.rsi state: conpipe-y - - type: DisposalJunction degrees: - 0 @@ -276,6 +302,11 @@ state_free: conpipe-y state_anchored: pipe-y state_broken: pipe-b + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5,-0.5,0.25,0.5" + layer: [ Underplating ] - type: entity id: DisposalBend @@ -287,7 +318,6 @@ drawdepth: BelowFloor sprite: Constructible/Power/disposal.rsi state: conpipe-c - - type: DisposalBend - type: Appearance visuals: @@ -295,3 +325,8 @@ state_free: conpipe-c state_anchored: pipe-c state_broken: pipe-b + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.5,-0.5,0.25,0.25" + layer: [ Underplating ] diff --git a/Resources/Prototypes/Entities/Constructible/Walls/bar_sign.yml b/Resources/Prototypes/Entities/Constructible/Walls/bar_sign.yml index 78337186da..9f396756c2 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/bar_sign.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/bar_sign.yml @@ -5,11 +5,14 @@ - type: Clickable - type: InteractionOutline - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.45, -0.95, 0.45, 1" + layer: [ Passable ] - type: Sprite drawdepth: WallTops sprite: Constructible/Misc/barsign.rsi state: empty - - type: PowerReceiver - type: BarSign @@ -43,5 +46,10 @@ components: - type: BarSign current: CyberSylph + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.45, -0.95, 0.95, 1.5" + layer: [ Passable ] - type: Sprite drawdepth: Ghosts diff --git a/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml b/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml index feec88e639..1496013a3d 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml @@ -5,6 +5,10 @@ - type: Clickable - type: InteractionOutline - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "-0.45, -0.15, 0.45, 0.35" + layer: [ Passable ] - type: LoopingSound - type: Sprite sprite: Constructible/Lighting/light_tube.rsi @@ -48,6 +52,11 @@ energy: 1.0 enabled: false offset: "-0.5, 0" + - type: Physics + shapes: + - !type:PhysShapeAabb + bounds: "0, 0.1, 0.25, 0.1" + layer: [ Passable ] - type: PoweredLight bulb: Bulb - type: PowerReceiver diff --git a/Resources/Prototypes/Entities/Singularity/emitter.yml b/Resources/Prototypes/Entities/Singularity/emitter.yml new file mode 100644 index 0000000000..de1b6ae549 --- /dev/null +++ b/Resources/Prototypes/Entities/Singularity/emitter.yml @@ -0,0 +1,63 @@ +- type: entity + name: Emitter + description: "A machine that fires bolts of energy, used for powering containment fields at a safe distance." + id: Emitter + placement: + mode: SnapgridCenter + components: + - type: InteractionOutline + - type: Clickable + - type: Physics + anchored: true + shapes: + - !type:PhysShapeAabb + bounds: "-0.5, -0.5, 0.5, 0.5" + layer: [MobMask, Opaque] + - type: SnapGrid + offset: Center + - type: Sprite + sprite: Constructible/Power/emitter.rsi + layers: + - state: emitter2 + - state: emitter-beam + shader: unshaded + visible: false + - state: emitter-lock + shader: unshaded + visible: false + - type: Emitter + - type: PowerConsumer + voltage: Medium + - type: NodeContainer + nodes: + - !type:AdjacentNode + nodeGroupID: MVPower + - type: Anchorable + - type: Pullable + - type: Appearance + visuals: + - type: EmitterVisualizer + - type: AccessReader + access: [[ "Engineering" ]] + + +- type: entity + name: Emitter Bolt + description: "A bolt of energy." + id: EmitterBolt + parent: BulletBase + components: + - type: Sprite + sprite: Constructible/Power/emitter.rsi + state: '' + layers: + - state: emitter_projectile + shader: unshaded + - type: Icon + sprite: Constructible/Power/emitter.rsi + state: emitter_projectile + - type: EmitterBoltComponent + - type: Projectile + soundHit: /Audio/Weapons/Guns/Hits/bullet_hit.ogg + damages: + Heat: 20 diff --git a/Resources/Prototypes/Entities/singularity.yml b/Resources/Prototypes/Entities/singularity.yml new file mode 100644 index 0000000000..a466e02059 --- /dev/null +++ b/Resources/Prototypes/Entities/singularity.yml @@ -0,0 +1,133 @@ +- type: entity + name: "Gravitational Singularity" + description: "A mesmerizing swirl of darkness that sucks in everything.\nIf it's moving towards you, run." + id: Singularity + components: + - type: Clickable + - type: Physics + anchored: false + shapes: + - !type:PhysShapeCircle + radius: 0.5 + layer: [Impassable] + mask: + - AllMask + mass: 5 + - type: Singularity + - type: RadiationPulse + range: 15 + decay: false + - type: Sprite + sprite: Effects/Singularity/singularity_1.rsi + state: singularity_1 + - type: Icon + sprite: Effects/Singularity/singularity_1.rsi + state: singularity_1 + drawdepth: Items + +- type: entity + id: RadiationCollector + name: Radiation Collector + description: A machine that collects Radiation and turns it into power. + placement: + mode: SnapgridCenter + components: + - type: Clickable + - type: InteractionOutline + - type: Physics + anchored: true + shapes: + - !type:PhysShapeAabb + bounds: "-0.5, -0.5, 0.5, 0.5" + layer: [MobMask, Opaque] + - type: SnapGrid + offset: Center + - type: Sprite + sprite: Constructible/Power/radiation_collector.rsi + layers: + - state: ca_on + map: ["enum.RadiationCollectorVisualLayers.Main"] + - type: Appearance + visuals: + - type: RadiationCollectorVisualizer + - type: NodeContainer + nodes: + - !type:AdjacentNode + nodeGroupID: HVPower + - type: RadiationCollector + - type: Anchorable + - type: Pullable + +- type: entity + name: Containment Field Generator + description: "A machine that generates a containment field when powered by an emitter.\nKeeps the Singularity docile." + id: ContainmentFieldGenerator + placement: + mode: SnapgridCenter + components: + - type: InteractionOutline + - type: Clickable + - type: Physics + anchored: true + shapes: + - !type:PhysShapeAabb + bounds: "-0.5, -0.5, 0.5, 0.5" + layer: [MobMask, Opaque] + - type: SnapGrid + offset: Center + - type: Sprite + sprite: Constructible/Power/field_generator.rsi + state: Field_Gen + - type: Icon + sprite: Constructible/Power/field_generator.rsi + state: Field_Gen + - type: ContainmentFieldGenerator + - type: Anchorable + - type: Pullable + +- type: entity + name: Containment Field + description: "A containment field that repels gravitational singularities." + id: ContainmentField + placement: + mode: SnapgridCenter + components: + - type: InteractionOutline + - type: Clickable + - type: Physics + anchored: true + shapes: + - !type:PhysShapeAabb + bounds: "-0.5, -0.5, 0.5, 0.5" + layer: [MobMask, Opaque] + - type: SnapGrid + offset: Center + - type: Sprite + sprite: Effects/contain_f.rsi + state: Contain_F + - type: Icon + sprite: Effects/contain_f.rsi + state: Contain_F + - type: ContainmentField + +- type: entity + name: Gravitational Singularity Generator + description: An Odd Device which produces a Gravitational Singularity when set up. + id: SinguloGenerator + components: + - type: Sprite + sprite: Effects/Singularity/singulo_gen.rsi + state: singulo_gen + - type: SingularityGenerator + - type: InteractionOutline + - type: Clickable + - type: Physics + anchored: true + shapes: + - !type:PhysShapeAabb + bounds: "-0.5, -0.5, 0.5, 0.5" + layer: [ MobMask ] + - type: SnapGrid + offset: Center + - type: Anchorable + - type: Pullable diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/particle_accelerator.yml b/Resources/Prototypes/Recipes/Construction/Graphs/particle_accelerator.yml new file mode 100644 index 0000000000..ece30ad06e --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/particle_accelerator.yml @@ -0,0 +1,358 @@ +- type: constructionGraph + id: particleAcceleratorControlBox + start: start + graph: + - node: start + entity: ParticleAcceleratorControlBoxUnfinished + actions: + - !type:SpriteStateChange + state: "control_box" + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + steps: + - material: Cable + doAfter: 0.5 + + - node: wired + entity: ParticleAcceleratorControlBoxUnfinished + actions: + - !type:SpriteStateChange + state: "control_boxw" + edges: + - to: completed + conditions: + - !type:EntityAnchored {} + completed: + - !type:SnapToGrid {} + steps: + - tool: Screwing + doAfter: 0.5 + - to: start + conditions: + - !type:EntityAnchored {} + completed: + - !type:SpawnPrototype + prototype: ApcExtensionCableStack1 + steps: + - tool: Cutting + doAfter: 0.5 + + - node: completed + entity: ParticleAcceleratorControlBox + edges: + - to: wired + conditions: + - !type:EntityAnchored { } + steps: + - tool: Prying + doAfter: 0.5 + + +- type: constructionGraph + id: particleAcceleratorPowerBox + start: start + graph: + - node: start + entity: ParticleAcceleratorPowerBoxUnfinished + actions: + - !type:SpriteStateChange + state: "power_box" + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + steps: + - material: Cable + doAfter: 0.5 + + - node: wired + entity: ParticleAcceleratorPowerBoxUnfinished + actions: + - !type:SpriteStateChange + state: "power_boxw" + edges: + - to: completed + conditions: + - !type:EntityAnchored {} + completed: + - !type:SnapToGrid {} + steps: + - tool: Screwing + doAfter: 0.5 + - to: start + conditions: + - !type:EntityAnchored {} + completed: + - !type:SpawnPrototype + prototype: ApcExtensionCableStack1 + steps: + - tool: Cutting + doAfter: 0.5 + + - node: completed + entity: ParticleAcceleratorPowerBox + edges: + - to: wired + conditions: + - !type:EntityAnchored { } + steps: + - tool: Screwing + doAfter: 0.5 + + +- type: constructionGraph + id: particleAcceleratorFuelChamber + start: start + graph: + - node: start + entity: ParticleAcceleratorFuelChamberUnfinished + actions: + - !type:SpriteStateChange + state: "fuel_chamber" + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + steps: + - material: Cable + doAfter: 0.5 + + - node: wired + entity: ParticleAcceleratorFuelChamberUnfinished + actions: + - !type:SpriteStateChange + state: "fuel_chamberw" + edges: + - to: completed + conditions: + - !type:EntityAnchored {} + completed: + - !type:SnapToGrid {} + steps: + - tool: Screwing + doAfter: 0.5 + - to: start + conditions: + - !type:EntityAnchored {} + completed: + - !type:SpawnPrototype + prototype: ApcExtensionCableStack1 + steps: + - tool: Cutting + doAfter: 0.5 + + - node: completed + entity: ParticleAcceleratorFuelChamber + edges: + - to: wired + conditions: + - !type:EntityAnchored { } + steps: + - tool: Screwing + doAfter: 0.5 + +- type: constructionGraph + id: particleAcceleratorEndCap + start: start + graph: + - node: start + entity: ParticleAcceleratorEndCapUnfinished + actions: + - !type:SpriteStateChange + state: "end_cap" + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + steps: + - material: Cable + doAfter: 0.5 + + - node: wired + entity: ParticleAcceleratorEndCapUnfinished + actions: + - !type:SpriteStateChange + state: "end_capw" + edges: + - to: completed + conditions: + - !type:EntityAnchored {} + completed: + - !type:SnapToGrid {} + steps: + - tool: Screwing + doAfter: 0.5 + - to: start + conditions: + - !type:EntityAnchored {} + completed: + - !type:SpawnPrototype + prototype: ApcExtensionCableStack1 + steps: + - tool: Cutting + doAfter: 0.5 + + - node: completed + entity: ParticleAcceleratorEndCap + edges: + - to: wired + conditions: + - !type:EntityAnchored { } + steps: + - tool: Screwing + doAfter: 0.5 + +- type: constructionGraph + id: particleAcceleratorEmitterLeft + start: start + graph: + - node: start + entity: ParticleAcceleratorEmitterLeftUnfinished + actions: + - !type:SpriteStateChange + state: "emitter_left" + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + steps: + - material: Cable + doAfter: 0.5 + + - node: wired + entity: ParticleAcceleratorEmitterLeftUnfinished + actions: + - !type:SpriteStateChange + state: "emitter_leftw" + edges: + - to: completed + conditions: + - !type:EntityAnchored {} + completed: + - !type:SnapToGrid {} + steps: + - tool: Screwing + doAfter: 0.5 + - to: start + conditions: + - !type:EntityAnchored {} + completed: + - !type:SpawnPrototype + prototype: ApcExtensionCableStack1 + steps: + - tool: Cutting + doAfter: 0.5 + + - node: completed + entity: ParticleAcceleratorEmitterLeft + edges: + - to: wired + conditions: + - !type:EntityAnchored { } + steps: + - tool: Screwing + doAfter: 0.5 + +- type: constructionGraph + id: particleAcceleratorEmitterCenter + start: start + graph: + - node: start + entity: ParticleAcceleratorEmitterCenterUnfinished + actions: + - !type:SpriteStateChange + state: "emitter_center" + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + steps: + - material: Cable + doAfter: 0.5 + + - node: wired + entity: ParticleAcceleratorEmitterCenterUnfinished + actions: + - !type:SpriteStateChange + state: "emitter_centerw" + edges: + - to: completed + conditions: + - !type:EntityAnchored {} + completed: + - !type:SnapToGrid {} + steps: + - tool: Screwing + doAfter: 0.5 + - to: start + conditions: + - !type:EntityAnchored {} + completed: + - !type:SpawnPrototype + prototype: ApcExtensionCableStack1 + steps: + - tool: Cutting + doAfter: 0.5 + + - node: completed + entity: ParticleAcceleratorEmitterCenter + edges: + - to: wired + conditions: + - !type:EntityAnchored { } + steps: + - tool: Screwing + doAfter: 0.5 + +- type: constructionGraph + id: particleAcceleratorEmitterRight + start: start + graph: + - node: start + entity: ParticleAcceleratorEmitterRightUnfinished + actions: + - !type:SpriteStateChange + state: "emitter_right" + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + steps: + - material: Cable + doAfter: 0.5 + + - node: wired + entity: ParticleAcceleratorEmitterRightUnfinished + actions: + - !type:SpriteStateChange + state: "emitter_rightw" + edges: + - to: completed + conditions: + - !type:EntityAnchored {} + completed: + - !type:SnapToGrid {} + steps: + - tool: Screwing + doAfter: 0.5 + - to: start + conditions: + - !type:EntityAnchored {} + completed: + - !type:SpawnPrototype + prototype: ApcExtensionCableStack1 + steps: + - tool: Cutting + doAfter: 0.5 + + - node: completed + entity: ParticleAcceleratorEmitterRight + edges: + - to: wired + conditions: + - !type:EntityAnchored { } + steps: + - tool: Screwing + doAfter: 0.5 diff --git a/Resources/Prototypes/Shaders/greyscale.yml b/Resources/Prototypes/Shaders/greyscale.yml new file mode 100644 index 0000000000..176893ab89 --- /dev/null +++ b/Resources/Prototypes/Shaders/greyscale.yml @@ -0,0 +1,4 @@ +- type: shader + id: Greyscale + kind: source + path: "/Textures/Shaders/greyscale.swsl" diff --git a/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box.png b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box.png new file mode 100644 index 0000000000..b0f742dbe5 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box.png differ diff --git a/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp.png b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp.png new file mode 100644 index 0000000000..7fb11ea3d6 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp.png differ diff --git a/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp0.png b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp0.png new file mode 100644 index 0000000000..b7316a64a7 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp0.png differ diff --git a/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp1.png b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp1.png new file mode 100644 index 0000000000..065b9d36d6 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp1.png differ diff --git a/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp2.png b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp2.png new file mode 100644 index 0000000000..ce98927bd4 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp2.png differ diff --git a/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp3.png b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp3.png new file mode 100644 index 0000000000..6faa6c5088 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_box_unlitp3.png differ diff --git a/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_boxc.png b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_boxc.png new file mode 100644 index 0000000000..cb9e35f3b7 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_boxc.png differ diff --git a/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_boxw.png b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_boxw.png new file mode 100644 index 0000000000..6e0409e9cf Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/control_box.rsi/control_boxw.png differ diff --git a/Resources/Textures/Constructible/Power/PA/control_box.rsi/meta.json b/Resources/Textures/Constructible/Power/PA/control_box.rsi/meta.json new file mode 100644 index 0000000000..1e22cb2d96 --- /dev/null +++ b/Resources/Textures/Constructible/Power/PA/control_box.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "states": [{"name": "control_box", "directions": 1, "delays": [[1.0]]},{"name": "control_boxc", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "control_box_unlitp", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "control_box_unlitp0", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "control_box_unlitp1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "control_box_unlitp2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "control_box_unlitp3", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "control_boxw", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} diff --git a/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center.png b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center.png new file mode 100644 index 0000000000..1b56db7025 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp.png b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp.png new file mode 100644 index 0000000000..2b3c1cce63 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp0.png b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp0.png new file mode 100644 index 0000000000..c34a3c568e Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp0.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp1.png b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp1.png new file mode 100644 index 0000000000..c1165932d0 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp1.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp2.png b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp2.png new file mode 100644 index 0000000000..338dee3ed2 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp2.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp3.png b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp3.png new file mode 100644 index 0000000000..c5c3bdb0ee Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_center_unlitp3.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_centerc.png b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_centerc.png new file mode 100644 index 0000000000..68ac450053 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_centerc.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_centerw.png b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_centerw.png new file mode 100644 index 0000000000..f5f28201cc Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/emitter_centerw.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/meta.json b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/meta.json new file mode 100644 index 0000000000..c5d382f096 --- /dev/null +++ b/Resources/Textures/Constructible/Power/PA/emitter_center.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "states": [{"name": "emitter_center", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_centerc", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_center_unlitp", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_center_unlitp0", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_center_unlitp1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_center_unlitp2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_center_unlitp3", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_centerw", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} diff --git a/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left.png b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left.png new file mode 100644 index 0000000000..4451d486fd Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp.png b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp.png new file mode 100644 index 0000000000..2b3c1cce63 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp0.png b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp0.png new file mode 100644 index 0000000000..4f3141c5fb Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp0.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp1.png b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp1.png new file mode 100644 index 0000000000..e161218ec8 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp1.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp2.png b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp2.png new file mode 100644 index 0000000000..97ae2e0273 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp2.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp3.png b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp3.png new file mode 100644 index 0000000000..3dce6b4d2f Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_left_unlitp3.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_leftc.png b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_leftc.png new file mode 100644 index 0000000000..cdb99dfde0 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_leftc.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_leftw.png b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_leftw.png new file mode 100644 index 0000000000..df5a30d404 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/emitter_leftw.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/meta.json b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/meta.json new file mode 100644 index 0000000000..4177e9af75 --- /dev/null +++ b/Resources/Textures/Constructible/Power/PA/emitter_left.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "states": [{"name": "emitter_left", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_leftc", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_left_unlitp", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_left_unlitp0", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_left_unlitp1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_left_unlitp2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_left_unlitp3", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_leftw", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} diff --git a/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right.png b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right.png new file mode 100644 index 0000000000..a02a371563 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp.png b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp.png new file mode 100644 index 0000000000..2b3c1cce63 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp0.png b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp0.png new file mode 100644 index 0000000000..d82fa071a6 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp0.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp1.png b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp1.png new file mode 100644 index 0000000000..6a351133a5 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp1.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp2.png b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp2.png new file mode 100644 index 0000000000..4e45f2dd04 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp2.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp3.png b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp3.png new file mode 100644 index 0000000000..28dffcf55e Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_right_unlitp3.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_rightc.png b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_rightc.png new file mode 100644 index 0000000000..14158c6f69 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_rightc.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_rightw.png b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_rightw.png new file mode 100644 index 0000000000..19dc6de461 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/emitter_rightw.png differ diff --git a/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/meta.json b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/meta.json new file mode 100644 index 0000000000..8e31a7922b --- /dev/null +++ b/Resources/Textures/Constructible/Power/PA/emitter_right.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "states": [{"name": "emitter_right", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_rightc", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_right_unlitp", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_right_unlitp0", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_right_unlitp1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_right_unlitp2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_right_unlitp3", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "emitter_rightw", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} diff --git a/Resources/Textures/Constructible/Power/PA/end_cap.rsi/end_cap.png b/Resources/Textures/Constructible/Power/PA/end_cap.rsi/end_cap.png new file mode 100644 index 0000000000..91f3b12767 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/end_cap.rsi/end_cap.png differ diff --git a/Resources/Textures/Constructible/Power/PA/end_cap.rsi/end_capc.png b/Resources/Textures/Constructible/Power/PA/end_cap.rsi/end_capc.png new file mode 100644 index 0000000000..47cb9802c7 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/end_cap.rsi/end_capc.png differ diff --git a/Resources/Textures/Constructible/Power/PA/end_cap.rsi/end_capw.png b/Resources/Textures/Constructible/Power/PA/end_cap.rsi/end_capw.png new file mode 100644 index 0000000000..cd4b9fbd38 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/end_cap.rsi/end_capw.png differ diff --git a/Resources/Textures/Constructible/Power/PA/end_cap.rsi/meta.json b/Resources/Textures/Constructible/Power/PA/end_cap.rsi/meta.json new file mode 100644 index 0000000000..8ba5704628 --- /dev/null +++ b/Resources/Textures/Constructible/Power/PA/end_cap.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "states": [{"name": "end_cap", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "end_capc", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "end_capw", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} diff --git a/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber.png b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber.png new file mode 100644 index 0000000000..0ba7ce55bc Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber.png differ diff --git a/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp.png b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp.png new file mode 100644 index 0000000000..a32b339fd8 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp.png differ diff --git a/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp0.png b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp0.png new file mode 100644 index 0000000000..8d822ae6d2 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp0.png differ diff --git a/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp1.png b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp1.png new file mode 100644 index 0000000000..781e214182 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp1.png differ diff --git a/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp2.png b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp2.png new file mode 100644 index 0000000000..d10bee925a Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp2.png differ diff --git a/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp3.png b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp3.png new file mode 100644 index 0000000000..2fee6e0daa Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamber_unlitp3.png differ diff --git a/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamberc.png b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamberc.png new file mode 100644 index 0000000000..3867b13ec1 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamberc.png differ diff --git a/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamberw.png b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamberw.png new file mode 100644 index 0000000000..69b72b24a3 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/fuel_chamberw.png differ diff --git a/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/meta.json b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/meta.json new file mode 100644 index 0000000000..50256eca64 --- /dev/null +++ b/Resources/Textures/Constructible/Power/PA/fuel_chamber.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "states": [{"name": "fuel_chamber", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "fuel_chamberc", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "fuel_chamber_unlitp", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "fuel_chamber_unlitp0", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "fuel_chamber_unlitp1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "fuel_chamber_unlitp2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "fuel_chamber_unlitp3", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "fuel_chamberw", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} diff --git a/Resources/Textures/Constructible/Power/PA/particle.rsi/meta.json b/Resources/Textures/Constructible/Power/PA/particle.rsi/meta.json new file mode 100644 index 0000000000..fb8e4970cd --- /dev/null +++ b/Resources/Textures/Constructible/Power/PA/particle.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "states": [{"name": "particle0", "directions": 4, "delays": [[0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1]]}, {"name": "particle1", "directions": 4, "delays": [[0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1]]}, {"name": "particle2", "directions": 4, "delays": [[0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1]]}, {"name": "particle3", "directions": 4, "delays": [[0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1]]}]} \ No newline at end of file diff --git a/Resources/Textures/Constructible/Power/PA/particle.rsi/particle0.png b/Resources/Textures/Constructible/Power/PA/particle.rsi/particle0.png new file mode 100644 index 0000000000..2ac765f12a Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/particle.rsi/particle0.png differ diff --git a/Resources/Textures/Constructible/Power/PA/particle.rsi/particle1.png b/Resources/Textures/Constructible/Power/PA/particle.rsi/particle1.png new file mode 100644 index 0000000000..2ac765f12a Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/particle.rsi/particle1.png differ diff --git a/Resources/Textures/Constructible/Power/PA/particle.rsi/particle2.png b/Resources/Textures/Constructible/Power/PA/particle.rsi/particle2.png new file mode 100644 index 0000000000..2ac765f12a Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/particle.rsi/particle2.png differ diff --git a/Resources/Textures/Constructible/Power/PA/particle.rsi/particle3.png b/Resources/Textures/Constructible/Power/PA/particle.rsi/particle3.png new file mode 100644 index 0000000000..ab1131a565 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/particle.rsi/particle3.png differ diff --git a/Resources/Textures/Constructible/Power/PA/power_box.rsi/meta.json b/Resources/Textures/Constructible/Power/PA/power_box.rsi/meta.json new file mode 100644 index 0000000000..c06afab80b --- /dev/null +++ b/Resources/Textures/Constructible/Power/PA/power_box.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "states": [{"name": "power_box", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "power_boxc", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "power_box_unlitp", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "power_box_unlitp0", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "power_box_unlitp1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "power_box_unlitp2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "power_box_unlitp3", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "power_boxw", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} diff --git a/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box.png b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box.png new file mode 100644 index 0000000000..a5abb283a1 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box.png differ diff --git a/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp.png b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp.png new file mode 100644 index 0000000000..a928000e47 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp.png differ diff --git a/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp0.png b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp0.png new file mode 100644 index 0000000000..289099a071 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp0.png differ diff --git a/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp1.png b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp1.png new file mode 100644 index 0000000000..517a8eb736 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp1.png differ diff --git a/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp2.png b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp2.png new file mode 100644 index 0000000000..68e76c220c Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp2.png differ diff --git a/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp3.png b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp3.png new file mode 100644 index 0000000000..bdded3b3b3 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_box_unlitp3.png differ diff --git a/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_boxc.png b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_boxc.png new file mode 100644 index 0000000000..e290f086c6 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_boxc.png differ diff --git a/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_boxw.png b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_boxw.png new file mode 100644 index 0000000000..98a5804157 Binary files /dev/null and b/Resources/Textures/Constructible/Power/PA/power_box.rsi/power_boxw.png differ diff --git a/Resources/Textures/Constructible/Power/emitter.rsi/emitter-beam.png b/Resources/Textures/Constructible/Power/emitter.rsi/emitter-beam.png new file mode 100644 index 0000000000..2b849ce52f Binary files /dev/null and b/Resources/Textures/Constructible/Power/emitter.rsi/emitter-beam.png differ diff --git a/Resources/Textures/Constructible/Power/emitter.rsi/emitter-lock.png b/Resources/Textures/Constructible/Power/emitter.rsi/emitter-lock.png new file mode 100644 index 0000000000..1afb76f9b9 Binary files /dev/null and b/Resources/Textures/Constructible/Power/emitter.rsi/emitter-lock.png differ diff --git a/Resources/Textures/Constructible/Power/emitter.rsi/emitter-underpowered.png b/Resources/Textures/Constructible/Power/emitter.rsi/emitter-underpowered.png new file mode 100644 index 0000000000..255b57873d Binary files /dev/null and b/Resources/Textures/Constructible/Power/emitter.rsi/emitter-underpowered.png differ diff --git a/Resources/Textures/Constructible/Power/emitter.rsi/emitter0.png b/Resources/Textures/Constructible/Power/emitter.rsi/emitter0.png new file mode 100644 index 0000000000..ef59df7509 Binary files /dev/null and b/Resources/Textures/Constructible/Power/emitter.rsi/emitter0.png differ diff --git a/Resources/Textures/Constructible/Power/emitter.rsi/emitter1.png b/Resources/Textures/Constructible/Power/emitter.rsi/emitter1.png new file mode 100644 index 0000000000..babb79a194 Binary files /dev/null and b/Resources/Textures/Constructible/Power/emitter.rsi/emitter1.png differ diff --git a/Resources/Textures/Constructible/Power/emitter.rsi/emitter2.png b/Resources/Textures/Constructible/Power/emitter.rsi/emitter2.png new file mode 100644 index 0000000000..9b85d68e94 Binary files /dev/null and b/Resources/Textures/Constructible/Power/emitter.rsi/emitter2.png differ diff --git a/Resources/Textures/Constructible/Power/emitter.rsi/emitter_projectile.png b/Resources/Textures/Constructible/Power/emitter.rsi/emitter_projectile.png new file mode 100644 index 0000000000..a1c706d409 Binary files /dev/null and b/Resources/Textures/Constructible/Power/emitter.rsi/emitter_projectile.png differ diff --git a/Resources/Textures/Constructible/Power/emitter.rsi/meta.json b/Resources/Textures/Constructible/Power/emitter.rsi/meta.json new file mode 100644 index 0000000000..a880d0e5b6 --- /dev/null +++ b/Resources/Textures/Constructible/Power/emitter.rsi/meta.json @@ -0,0 +1 @@ +{"version":1,"size":{"x":32,"y":32},"license":"CC-BY-SA-3.0","copyright":"Taken from https://github.com/tgstation/tgstation","states":[{"name":"emitter-beam","directions":4,"delays":[[0.1],[0.1],[0.1],[0.1]]},{"name":"emitter-underpowered","directions":4,"delays":[[0.1,0.1],[0.1,0.1],[0.1,0.1],[0.1,0.1]]},{"name":"emitter-lock","directions":4,"delays":[[1.0],[1.0],[1.0],[1.0]]},{"name":"emitter0","directions":4,"delays":[[0.1],[0.1],[0.1],[0.1]]},{"name":"emitter1","directions":4,"delays":[[1.0],[1.0],[1.0],[1.0]]},{"name":"emitter2","directions":4,"delays":[[1.0],[1.0],[1.0],[1.0]]},{"name":"emitter_projectile","directions":1,"delays":[[1.0]]}]} \ No newline at end of file diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/+a1.png b/Resources/Textures/Constructible/Power/field_generator.rsi/+a1.png new file mode 100644 index 0000000000..7d5fc9ef89 Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/+a1.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/+a2.png b/Resources/Textures/Constructible/Power/field_generator.rsi/+a2.png new file mode 100644 index 0000000000..78aee61a4f Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/+a2.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/+a3.png b/Resources/Textures/Constructible/Power/field_generator.rsi/+a3.png new file mode 100644 index 0000000000..104e27493c Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/+a3.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/+on.png b/Resources/Textures/Constructible/Power/field_generator.rsi/+on.png new file mode 100644 index 0000000000..9b69664237 Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/+on.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/+p1.png b/Resources/Textures/Constructible/Power/field_generator.rsi/+p1.png new file mode 100644 index 0000000000..ca2219ddf3 Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/+p1.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/+p2.png b/Resources/Textures/Constructible/Power/field_generator.rsi/+p2.png new file mode 100644 index 0000000000..9246a7ea9b Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/+p2.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/+p3.png b/Resources/Textures/Constructible/Power/field_generator.rsi/+p3.png new file mode 100644 index 0000000000..c9a1ce32bb Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/+p3.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/+p4.png b/Resources/Textures/Constructible/Power/field_generator.rsi/+p4.png new file mode 100644 index 0000000000..d75c431c8d Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/+p4.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/+p5.png b/Resources/Textures/Constructible/Power/field_generator.rsi/+p5.png new file mode 100644 index 0000000000..0b3c771a33 Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/+p5.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/+p6.png b/Resources/Textures/Constructible/Power/field_generator.rsi/+p6.png new file mode 100644 index 0000000000..a3cbb40d24 Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/+p6.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/Field_Gen.png b/Resources/Textures/Constructible/Power/field_generator.rsi/Field_Gen.png new file mode 100644 index 0000000000..c47df306f3 Binary files /dev/null and b/Resources/Textures/Constructible/Power/field_generator.rsi/Field_Gen.png differ diff --git a/Resources/Textures/Constructible/Power/field_generator.rsi/meta.json b/Resources/Textures/Constructible/Power/field_generator.rsi/meta.json new file mode 100644 index 0000000000..5afd9bc022 --- /dev/null +++ b/Resources/Textures/Constructible/Power/field_generator.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/tgstation/tgstation", "states": [{"name": "+a1", "directions": 1, "delays": [[1.0]]}, {"name": "+a2", "directions": 1, "delays": [[1.0]]}, {"name": "+a3", "directions": 1, "delays": [[1.0]]}, {"name": "+on", "directions": 1, "delays": [[1.0]]}, {"name": "+p1", "directions": 1, "delays": [[1.0]]}, {"name": "+p2", "directions": 1, "delays": [[1.0]]}, {"name": "+p3", "directions": 1, "delays": [[1.0]]}, {"name": "+p4", "directions": 1, "delays": [[1.0]]}, {"name": "+p5", "directions": 1, "delays": [[1.0]]}, {"name": "+p6", "directions": 1, "delays": [[1.0]]}, {"name": "Field_Gen", "directions": 1, "delays": [[1.0]]}]} \ No newline at end of file diff --git a/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_active.png b/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_active.png new file mode 100644 index 0000000000..39dbf8d014 Binary files /dev/null and b/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_active.png differ diff --git a/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_deactive.png b/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_deactive.png new file mode 100644 index 0000000000..81f95545f4 Binary files /dev/null and b/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_deactive.png differ diff --git a/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_off.png b/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_off.png new file mode 100644 index 0000000000..fa2741995e Binary files /dev/null and b/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_off.png differ diff --git a/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_on.png b/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_on.png new file mode 100644 index 0000000000..122c0ce45f Binary files /dev/null and b/Resources/Textures/Constructible/Power/radiation_collector.rsi/ca_on.png differ diff --git a/Resources/Textures/Constructible/Power/radiation_collector.rsi/cu.png b/Resources/Textures/Constructible/Power/radiation_collector.rsi/cu.png new file mode 100644 index 0000000000..896c844149 Binary files /dev/null and b/Resources/Textures/Constructible/Power/radiation_collector.rsi/cu.png differ diff --git a/Resources/Textures/Constructible/Power/radiation_collector.rsi/meta.json b/Resources/Textures/Constructible/Power/radiation_collector.rsi/meta.json new file mode 100644 index 0000000000..f6d83c3c76 --- /dev/null +++ b/Resources/Textures/Constructible/Power/radiation_collector.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/goonstation/goonstation-2020", "states": [{"name": "ca_off", "directions": 1, "delays": [[1.0]]}, {"name": "ca_on", "directions": 1, "delays": [[1.0]]}, {"name": "ca_active", "directions": 1, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "ca_deactive", "directions": 1, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "cu", "directions": 1, "delays": [[1.0]]}]} diff --git a/Resources/Textures/Effects/Singularity/singularity_1.rsi/meta.json b/Resources/Textures/Effects/Singularity/singularity_1.rsi/meta.json new file mode 100644 index 0000000000..265b9125e9 --- /dev/null +++ b/Resources/Textures/Effects/Singularity/singularity_1.rsi/meta.json @@ -0,0 +1 @@ +{"version":1,"size":{"x":32,"y":32},"copyright":"Taken from https://github.com/vgstation-coders/vgstation13/blob/8eef5a676f66551bd0fb40d506486a6b3b2b0f1a/icons/obj/singularity.dmi","license":"CC-BY-SA-3.0","states":[{"name":"singularity_1","directions":1,"delays":[[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1]]}]} diff --git a/Resources/Textures/Effects/Singularity/singularity_1.rsi/singularity_1.png b/Resources/Textures/Effects/Singularity/singularity_1.rsi/singularity_1.png new file mode 100644 index 0000000000..4a862b604d Binary files /dev/null and b/Resources/Textures/Effects/Singularity/singularity_1.rsi/singularity_1.png differ diff --git a/Resources/Textures/Effects/Singularity/singularity_2.rsi/meta.json b/Resources/Textures/Effects/Singularity/singularity_2.rsi/meta.json new file mode 100644 index 0000000000..44c79e7964 --- /dev/null +++ b/Resources/Textures/Effects/Singularity/singularity_2.rsi/meta.json @@ -0,0 +1 @@ +{"version":1,"size":{"x":96,"y":96},"copyright":"Taken from https://github.com/vgstation-coders/vgstation13/blob/8eef5a676f66551bd0fb40d506486a6b3b2b0f1a/icons/effects/96x96.dmi","license":"CC-BY-SA-3.0","states":[{"name":"singularity_2","directions":1,"delays":[[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1]]}]} diff --git a/Resources/Textures/Effects/Singularity/singularity_2.rsi/singularity_2.png b/Resources/Textures/Effects/Singularity/singularity_2.rsi/singularity_2.png new file mode 100644 index 0000000000..b4e91a1136 Binary files /dev/null and b/Resources/Textures/Effects/Singularity/singularity_2.rsi/singularity_2.png differ diff --git a/Resources/Textures/Effects/Singularity/singularity_3.rsi/meta.json b/Resources/Textures/Effects/Singularity/singularity_3.rsi/meta.json new file mode 100644 index 0000000000..2d500fd12f --- /dev/null +++ b/Resources/Textures/Effects/Singularity/singularity_3.rsi/meta.json @@ -0,0 +1 @@ +{"version":1,"size":{"x":160,"y":160},"copyright":"Taken from https://github.com/vgstation-coders/vgstation13/blob/8eef5a676f66551bd0fb40d506486a6b3b2b0f1a/icons/effects/160x160.dmi","license":"CC-BY-SA-3.0","states":[{"name":"singularity_3","directions":1,"delays":[[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1]]}]} diff --git a/Resources/Textures/Effects/Singularity/singularity_3.rsi/singularity_3.png b/Resources/Textures/Effects/Singularity/singularity_3.rsi/singularity_3.png new file mode 100644 index 0000000000..3f334e0ab2 Binary files /dev/null and b/Resources/Textures/Effects/Singularity/singularity_3.rsi/singularity_3.png differ diff --git a/Resources/Textures/Effects/Singularity/singularity_4.rsi/meta.json b/Resources/Textures/Effects/Singularity/singularity_4.rsi/meta.json new file mode 100644 index 0000000000..1313bc34c5 --- /dev/null +++ b/Resources/Textures/Effects/Singularity/singularity_4.rsi/meta.json @@ -0,0 +1 @@ +{"version":1,"size":{"x":224,"y":224},"copyright":"Taken from https://github.com/vgstation-coders/vgstation13/blob/8eef5a676f66551bd0fb40d506486a6b3b2b0f1a/icons/effects/224x224.dmi","license":"CC-BY-SA-3.0","states":[{"name":"singularity_4","directions":1,"delays":[[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1]]}]} diff --git a/Resources/Textures/Effects/Singularity/singularity_4.rsi/singularity_4.png b/Resources/Textures/Effects/Singularity/singularity_4.rsi/singularity_4.png new file mode 100644 index 0000000000..5c8107ef23 Binary files /dev/null and b/Resources/Textures/Effects/Singularity/singularity_4.rsi/singularity_4.png differ diff --git a/Resources/Textures/Effects/Singularity/singularity_5.rsi/meta.json b/Resources/Textures/Effects/Singularity/singularity_5.rsi/meta.json new file mode 100644 index 0000000000..ac26dee9ef --- /dev/null +++ b/Resources/Textures/Effects/Singularity/singularity_5.rsi/meta.json @@ -0,0 +1 @@ +{"version":1,"size":{"x":288,"y":288},"copyright":"Taken from https://github.com/vgstation-coders/vgstation13/blob/8eef5a676f66551bd0fb40d506486a6b3b2b0f1a/icons/effects/288x288.dmi","license":"CC-BY-SA-3.0","states":[{"name":"singularity_5","directions":1,"delays":[[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1]]}]} diff --git a/Resources/Textures/Effects/Singularity/singularity_5.rsi/singularity_5.png b/Resources/Textures/Effects/Singularity/singularity_5.rsi/singularity_5.png new file mode 100644 index 0000000000..03c49487ee Binary files /dev/null and b/Resources/Textures/Effects/Singularity/singularity_5.rsi/singularity_5.png differ diff --git a/Resources/Textures/Effects/Singularity/singularity_6.rsi/meta.json b/Resources/Textures/Effects/Singularity/singularity_6.rsi/meta.json new file mode 100644 index 0000000000..6f2a6d5cca --- /dev/null +++ b/Resources/Textures/Effects/Singularity/singularity_6.rsi/meta.json @@ -0,0 +1 @@ +{"version":1,"size":{"x":352,"y":352},"copyright":"Taken from https://github.com/vgstation-coders/vgstation13/blob/8eef5a676f66551bd0fb40d506486a6b3b2b0f1a/icons/effects/352x352.dmi","license":"CC-BY-SA-3.0","states":[{"name":"singularity_6","directions":1,"delays":[[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1]]}]} diff --git a/Resources/Textures/Effects/Singularity/singularity_6.rsi/singularity_6.png b/Resources/Textures/Effects/Singularity/singularity_6.rsi/singularity_6.png new file mode 100644 index 0000000000..3f187d4354 Binary files /dev/null and b/Resources/Textures/Effects/Singularity/singularity_6.rsi/singularity_6.png differ diff --git a/Resources/Textures/Effects/Singularity/singulo_gen.rsi/meta.json b/Resources/Textures/Effects/Singularity/singulo_gen.rsi/meta.json new file mode 100644 index 0000000000..7edbcf5edc --- /dev/null +++ b/Resources/Textures/Effects/Singularity/singulo_gen.rsi/meta.json @@ -0,0 +1 @@ +{"version":1,"size":{"x":32,"y":32},"copyright":"Taken from https://github.com/vgstation-coders/vgstation13/blob/68357f0b64a296d122ff1bdb2f13e9b0cb04a003/icons/obj/singularity.dmi","license":"CC-BY-SA-3.0","states":[{"name":"singulo_gen","directions":1,"delays":[[1]]}]} diff --git a/Resources/Textures/Effects/Singularity/singulo_gen.rsi/singulo_gen.png b/Resources/Textures/Effects/Singularity/singulo_gen.rsi/singulo_gen.png new file mode 100644 index 0000000000..493928c921 Binary files /dev/null and b/Resources/Textures/Effects/Singularity/singulo_gen.rsi/singulo_gen.png differ diff --git a/Resources/Textures/Effects/contain_f.rsi/Contain_F.png b/Resources/Textures/Effects/contain_f.rsi/Contain_F.png new file mode 100644 index 0000000000..bbd284cfcb Binary files /dev/null and b/Resources/Textures/Effects/contain_f.rsi/Contain_F.png differ diff --git a/Resources/Textures/Effects/contain_f.rsi/meta.json b/Resources/Textures/Effects/contain_f.rsi/meta.json new file mode 100644 index 0000000000..1603277d1d --- /dev/null +++ b/Resources/Textures/Effects/contain_f.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/tgstation/tgstation", "states": [{"name": "Contain_F", "directions": 4, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}]} \ No newline at end of file diff --git a/Resources/Textures/Shaders/greyscale.swsl b/Resources/Textures/Shaders/greyscale.swsl new file mode 100644 index 0000000000..918f86ae00 --- /dev/null +++ b/Resources/Textures/Shaders/greyscale.swsl @@ -0,0 +1,6 @@ +void fragment() { + highp vec4 color = zTexture(UV); + highp float grey = dot(color.rgb, vec3(0.299, 0.587, 0.114)); + + COLOR = vec4(vec3(grey), color.a); +} diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 9a400dd8e1..1a3de30377 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -48,6 +48,7 @@ OGL OOC OS + PA PCM PNG RSI @@ -126,6 +127,7 @@ True True True + True True True True