diff --git a/Content.Client/Singularity/Components/ContainmentFieldComponent.cs b/Content.Client/Singularity/Components/ContainmentFieldComponent.cs index 0d38d28e75..e90c9ee598 100644 --- a/Content.Client/Singularity/Components/ContainmentFieldComponent.cs +++ b/Content.Client/Singularity/Components/ContainmentFieldComponent.cs @@ -1,31 +1,7 @@ using Content.Shared.Singularity.Components; -using Robust.Client.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Log; -namespace Content.Client.Singularity.Components -{ - [RegisterComponent] - [ComponentReference(typeof(SharedContainmentFieldComponent))] - public sealed class ContainmentFieldComponent : SharedContainmentFieldComponent - { - // Jesus what is this code. - // Singulo cleanup WHEEENNN - private SpriteComponent? _spriteComponent; +namespace Content.Client.Singularity.Components; - protected override void Initialize() - { - base.Initialize(); - - if (!IoCManager.Resolve().TryGetComponent(Owner, out _spriteComponent)) - { - Logger.Error($"{nameof(ContainmentFieldComponent)} created without {nameof(SpriteComponent)}"); - } - else - { - _spriteComponent.NoRotation = true; - } - } - } -} +[RegisterComponent] +[ComponentReference(typeof(SharedContainmentFieldComponent))] +public sealed class ContainmentFieldComponent : SharedContainmentFieldComponent { } diff --git a/Content.Client/Singularity/Components/ContainmentFieldGeneratorComponent.cs b/Content.Client/Singularity/Components/ContainmentFieldGeneratorComponent.cs index 8039c22ec4..ee30bafbac 100644 --- a/Content.Client/Singularity/Components/ContainmentFieldGeneratorComponent.cs +++ b/Content.Client/Singularity/Components/ContainmentFieldGeneratorComponent.cs @@ -1,12 +1,6 @@ using Content.Shared.Singularity.Components; -using Robust.Shared.GameObjects; -namespace Content.Client.Singularity.Components -{ - [RegisterComponent] - [ComponentReference(typeof(SharedContainmentFieldGeneratorComponent))] - public sealed class ContainmentFieldGeneratorComponent : SharedContainmentFieldGeneratorComponent - { - - } -} +namespace Content.Client.Singularity.Components; +[RegisterComponent] +[ComponentReference(typeof(SharedContainmentFieldGeneratorComponent))] +public sealed class ContainmentFieldGeneratorComponent : SharedContainmentFieldGeneratorComponent { } diff --git a/Content.Server/Singularity/Components/ContainmentFieldComponent.cs b/Content.Server/Singularity/Components/ContainmentFieldComponent.cs index bd0b494f53..c2efde638c 100644 --- a/Content.Server/Singularity/Components/ContainmentFieldComponent.cs +++ b/Content.Server/Singularity/Components/ContainmentFieldComponent.cs @@ -1,11 +1,24 @@ using Content.Shared.Singularity.Components; -namespace Content.Server.Singularity.Components +namespace Content.Server.Singularity.Components; + +[RegisterComponent] +[ComponentReference(typeof(SharedContainmentFieldComponent))] +public sealed class ContainmentFieldComponent : SharedContainmentFieldComponent { - [RegisterComponent] - [ComponentReference(typeof(SharedContainmentFieldComponent))] - public sealed class ContainmentFieldComponent : SharedContainmentFieldComponent - { - public ContainmentFieldConnection? Parent; - } + /// + /// The throw force for the field if an entity collides with it + /// The lighter the mass the further it will throw. 5 mass will go about 4 tiles out, 70 mass goes only a couple tiles. + /// + [ViewVariables] + [DataField("throwForce")] + public float ThrowForce = 100f; + + /// + /// This shouldn't be at 99999 or higher to prevent the singulo glitching out + /// Will throw anything at the supplied mass or less that collides with the field. + /// + [ViewVariables] + [DataField("maxMass")] + public float MaxMass = 10000f; } diff --git a/Content.Server/Singularity/Components/ContainmentFieldConnection.cs b/Content.Server/Singularity/Components/ContainmentFieldConnection.cs deleted file mode 100644 index ccfc503a08..0000000000 --- a/Content.Server/Singularity/Components/ContainmentFieldConnection.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.Threading; -using Content.Server.Singularity.EntitySystems; -using Content.Shared.Singularity.Components; -using Robust.Server.GameObjects; -using Timer = Robust.Shared.Timing.Timer; - -namespace Content.Server.Singularity.Components -{ - public sealed class ContainmentFieldConnection : IDisposable - { - public ContainmentFieldGeneratorComponent Generator1; - public ContainmentFieldGeneratorComponent Generator2; - private readonly List _fields = new(); - private int _sharedEnergyPool; - private readonly CancellationTokenSource _powerDecreaseCancellationTokenSource = new(); - public int SharedEnergyPool - { - get => _sharedEnergyPool; - set - { - _sharedEnergyPool = Math.Clamp(value, 0, 25); - if (_sharedEnergyPool == 0) - { - Dispose(); - } - } - } - - public ContainmentFieldConnection(ContainmentFieldGeneratorComponent generator1, ContainmentFieldGeneratorComponent generator2) - { - Generator1 = generator1; - Generator2 = generator2; - - //generateFields - var pos1 = IoCManager.Resolve().GetComponent(generator1.Owner).Coordinates; - var pos2 = IoCManager.Resolve().GetComponent(generator2.Owner).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 (!IoCManager.Resolve().TryGetComponent(newEnt, out var containmentFieldComponent)) - { - Logger.Error("While creating Fields in ContainmentFieldConnection, a ContainmentField without a ContainmentFieldComponent was created. Deleting newly spawned ContainmentField..."); - IoCManager.Resolve().DeleteEntity(newEnt); - continue; - } - - containmentFieldComponent.Parent = this; - IoCManager.Resolve().GetComponent(newEnt).WorldRotation = IoCManager.Resolve().GetComponent(generator1.Owner).WorldRotation + dirVec.ToWorldAngle(); - - _fields.Add(newEnt); - currentOffset += dirVec; - } - - - Timer.SpawnRepeating(1000, () => { SharedEnergyPool--; }, _powerDecreaseCancellationTokenSource.Token); - } - - public bool CanRepel(SharedSingularityComponent toRepel) - { - var powerNeeded = 2 * toRepel.Level + 1; - - return _sharedEnergyPool > powerNeeded; - } - - public void Dispose() - { - _powerDecreaseCancellationTokenSource.Cancel(); - foreach (var field in _fields) - { - IoCManager.Resolve().DeleteEntity(field); - } - _fields.Clear(); - - RemoveConnection(this, Generator1); - RemoveConnection(this, Generator2); - } - - public void RemoveConnection(ContainmentFieldConnection? connection, ContainmentFieldGeneratorComponent component) - { - if (component.Connection1?.Item2 == connection) - { - component.Connection1 = null; - } - else if (component.Connection2?.Item2 == connection) - { - component.Connection2 = null; - } - else if (connection != null) - { - Logger.Error("RemoveConnection called on Containmentfieldgenerator with a connection that can't be found in its connections."); - } - if (IoCManager.Resolve().TryGetComponent(component.Owner, out var pointLightComponent)) - { - bool hasAnyConnection = (component.Connection1 != null) || (component.Connection2 != null); - pointLightComponent.Enabled = hasAnyConnection; - } - } - } -} diff --git a/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs b/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs index 40f34b1942..fa1e83eb82 100644 --- a/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs +++ b/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs @@ -1,33 +1,107 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; using Content.Shared.Physics; using Content.Shared.Singularity.Components; -using Robust.Server.GameObjects; -using Robust.Shared.Physics; +using Content.Shared.Tag; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Server.Singularity.Components +namespace Content.Server.Singularity.Components; +[RegisterComponent] +[ComponentReference(typeof(SharedContainmentFieldGeneratorComponent))] +public sealed class ContainmentFieldGeneratorComponent : SharedContainmentFieldGeneratorComponent { - [RegisterComponent] - [ComponentReference(typeof(SharedContainmentFieldGeneratorComponent))] - public sealed class ContainmentFieldGeneratorComponent : SharedContainmentFieldGeneratorComponent + private int _powerBuffer; + + /// + /// Store power with a cap. Decrease over time if not being powered from source. + /// + [ViewVariables] + [DataField("powerBuffer")] + public int PowerBuffer { - private int _powerBuffer; - - [ViewVariables] - public int PowerBuffer - { - get => _powerBuffer; - set => _powerBuffer = Math.Clamp(value, 0, 6); - } - - public Tuple? Connection1; - public Tuple? Connection2; - - [ViewVariables] - public bool Enabled; - - [ViewVariables] - public bool IsConnected; - + get => _powerBuffer; + set => _powerBuffer = Math.Clamp(value, 0, 25); //have this decrease over time if not hit by a bolt } + + /// + /// The minimum the field generator needs to start generating a connection + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("powerMinimum")] + public int PowerMinimum = 6; + + /// + /// How much power should this field generator receive from a collision + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("power")] + public int PowerReceived = 3; + + /// + /// How much power should this field generator lose if not powered? + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("powerLoss")] + public int PowerLoss = 2; + + /// + /// Used to check if it's received power recently. + /// + [ViewVariables] + [DataField("accumulator")] + public float Accumulator; + + /// + /// How many seconds should the generators wait before losing power? + /// + [ViewVariables] + [DataField("threshold")] + public float Threshold = 10f; + + /// + /// How many tiles should this field check before giving up? + /// + [ViewVariables] + [DataField("maxLength")] + public float MaxLength = 8F; + + /// + /// What collision should power this generator? + /// It really shouldn't be anything but an emitter bolt but it's here for fun. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("idTag", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string IDTag = "EmitterBolt"; + + /// + /// Is the generator toggled on? + /// + [ViewVariables] + public bool Enabled; + + /// + /// Is this generator connected to fields? + /// + [ViewVariables(VVAccess.ReadWrite)] + public bool IsConnected; + + /// + /// The masks the raycast should not go through + /// + [ViewVariables] + [DataField("collisionMask")] + public int CollisionMask = (int) (CollisionGroup.MobMask | CollisionGroup.Impassable | CollisionGroup.MachineMask); + + /// + /// A collection of connections that the generator has based on direction. + /// Stores a list of fields connected between generators in this direction. + /// + [ViewVariables] + public Dictionary)> Connections = new(); + + /// + /// What fields should this spawn? + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("createdField", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string CreatedField = "ContainmentField"; } diff --git a/Content.Server/Singularity/Components/SingularityGenerator.cs b/Content.Server/Singularity/Components/SingularityGeneratorComponent.cs similarity index 100% rename from Content.Server/Singularity/Components/SingularityGenerator.cs rename to Content.Server/Singularity/Components/SingularityGeneratorComponent.cs diff --git a/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs b/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs index fbe2182c85..e8c560d5e8 100644 --- a/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs +++ b/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs @@ -1,264 +1,415 @@ -using Content.Server.ParticleAccelerator.Components; -using Content.Server.Singularity.Components; -using Content.Shared.Physics; +using Content.Server.Singularity.Components; using Content.Shared.Singularity.Components; using Content.Shared.Tag; using Robust.Server.GameObjects; using Robust.Shared.Physics; using Robust.Shared.Physics.Dynamics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Server.Popups; using Content.Shared.Construction.Components; +using Content.Shared.Examine; using Content.Shared.Interaction; +using Content.Shared.Popups; +using Content.Shared.Throwing; using Robust.Shared.Player; -namespace Content.Server.Singularity.EntitySystems +namespace Content.Server.Singularity.EntitySystems; + +public sealed class ContainmentFieldGeneratorSystem : EntitySystem { - public sealed class ContainmentFieldGeneratorSystem : EntitySystem + [Dependency] private readonly TagSystem _tags = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; + + public override void Initialize() { - [Dependency] private readonly TagSystem _tags = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; + base.Initialize(); - public override void Initialize() + SubscribeLocalEvent(HandleGeneratorCollide); + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnInteract); + SubscribeLocalEvent(OnAnchorChanged); + SubscribeLocalEvent(OnReanchorEvent); + SubscribeLocalEvent(OnUnanchorAttempt); + SubscribeLocalEvent(OnComponentRemoved); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var generator in EntityQuery()) { - base.Initialize(); - - SubscribeLocalEvent(OnComponentRemoved); - SubscribeLocalEvent(HandleFieldCollide); - SubscribeLocalEvent(HandleGeneratorCollide); - SubscribeLocalEvent(HandleParticleCollide); - SubscribeLocalEvent(OnAnchorChanged); - SubscribeLocalEvent(OnUnanchorAttempt); - SubscribeLocalEvent(OnInteract); - } - - private void OnInteract(EntityUid uid, ContainmentFieldGeneratorComponent component, InteractHandEvent args) - { - if (args.Handled) + if (generator.PowerBuffer <= 0) //don't drain power if there's no power, or if it's somehow less than 0. return; - if (TryComp(component.Owner, out TransformComponent? transformComp) && transformComp.Anchored) - { - if (!component.Enabled) - TurnOn(component); - else if (component.Enabled && component.IsConnected) - { - _popupSystem.PopupEntity(Loc.GetString("comp-containment-anchor-warning"), args.User, Filter.Entities(args.User)); - return; - } - else - TurnOff(component); - } + generator.Accumulator += frameTime; - args.Handled = true; - } - - private void OnUnanchorAttempt(EntityUid uid, ContainmentFieldGeneratorComponent component, UnanchorAttemptEvent args) - { - if (component.Enabled) + if (generator.Accumulator >= generator.Threshold) { - _popupSystem.PopupEntity(Loc.GetString("comp-containment-anchor-warning"), args.User, Filter.Entities(args.User)); - args.Cancel(); + LosePower(generator.PowerLoss, generator); + generator.Accumulator -= generator.Threshold; } } + } - private void TurnOn(ContainmentFieldGeneratorComponent component) + #region Events + + /// + /// A generator receives power from a source colliding with it. + /// + private void HandleGeneratorCollide(EntityUid uid, ContainmentFieldGeneratorComponent component, StartCollideEvent args) + { + if (_tags.HasTag(args.OtherFixture.Body.Owner, component.IDTag)) { - component.Enabled = true; - _popupSystem.PopupEntity(Loc.GetString("comp-containment-turned-on"), component.Owner, Filter.Pvs(component.Owner)); + ReceivePower(component.PowerReceived, component); + component.Accumulator = 0f; } + } - private void TurnOff(ContainmentFieldGeneratorComponent component) - { - component.Enabled = false; - _popupSystem.PopupEntity(Loc.GetString("comp-containment-turned-off"), component.Owner, Filter.Pvs(component.Owner)); - } + private void OnExamine(EntityUid uid, ContainmentFieldGeneratorComponent component, ExaminedEvent args) + { + if (component.Enabled) + args.PushMarkup(Loc.GetString("comp-containment-on")); - private void OnComponentRemoved(EntityUid uid, ContainmentFieldGeneratorComponent component, ComponentRemove args) - { - component.Connection1?.Item2.Dispose(); - component.Connection2?.Item2.Dispose(); - } + else + args.PushMarkup(Loc.GetString("comp-containment-off")); + } - private void OnAnchorChanged(EntityUid uid, ContainmentFieldGeneratorComponent component, ref AnchorStateChangedEvent args) + private void OnInteract(EntityUid uid, ContainmentFieldGeneratorComponent component, InteractHandEvent args) + { + if (args.Handled) + return; + + if (TryComp(component.Owner, out TransformComponent? transformComp) && transformComp.Anchored) { - if (!args.Anchored) + if (!component.Enabled) + TurnOn(component); + else if (component.Enabled && component.IsConnected) { - component.Connection1?.Item2.Dispose(); - component.Connection2?.Item2.Dispose(); - } - } - - private void HandleParticleCollide(EntityUid uid, ParticleProjectileComponent component, StartCollideEvent args) - { - if (EntityManager.TryGetComponent(args.OtherFixture.Body.Owner, out var singularityGeneratorComponent)) - { - singularityGeneratorComponent.Power += component.State switch - { - ParticleAcceleratorPowerState.Standby => 0, - ParticleAcceleratorPowerState.Level0 => 1, - ParticleAcceleratorPowerState.Level1 => 2, - ParticleAcceleratorPowerState.Level2 => 4, - ParticleAcceleratorPowerState.Level3 => 8, - _ => 0 - }; - - EntityManager.QueueDeleteEntity(uid); - } - } - - private void HandleGeneratorCollide(EntityUid uid, ContainmentFieldGeneratorComponent component, StartCollideEvent args) - { - if (_tags.HasTag(args.OtherFixture.Body.Owner, "EmitterBolt")) - { - ReceivePower(6, component); - } - } - - private void HandleFieldCollide(EntityUid uid, ContainmentFieldComponent component, StartCollideEvent args) - { - if (component.Parent == null) - { - EntityManager.QueueDeleteEntity(uid); + _popupSystem.PopupEntity(Loc.GetString("comp-containment-toggle-warning"), args.User, Filter.Entities(args.User), PopupType.LargeCaution); return; } + else + TurnOff(component); } + args.Handled = true; + } - public void ReceivePower(int power, ContainmentFieldGeneratorComponent component) + private void OnAnchorChanged(EntityUid uid, ContainmentFieldGeneratorComponent component, ref AnchorStateChangedEvent args) + { + if (!args.Anchored) + RemoveConnections(component); + } + + private void OnReanchorEvent(EntityUid uid, ContainmentFieldGeneratorComponent component, ref ReAnchorEvent args) + { + GridCheck(component); + } + + private void OnUnanchorAttempt(EntityUid uid, ContainmentFieldGeneratorComponent component, + UnanchorAttemptEvent args) + { + if (component.Enabled) { - var totalPower = power + component.PowerBuffer; - var powerPerConnection = totalPower / 2; - var newBuffer = totalPower % 2; - TryPowerConnection(ref component.Connection1, ref newBuffer, powerPerConnection, component); - TryPowerConnection(ref component.Connection2, ref newBuffer, powerPerConnection, component); - - component.PowerBuffer = newBuffer; + _popupSystem.PopupEntity(Loc.GetString("comp-containment-anchor-warning"), args.User, Filter.Entities(args.User), PopupType.LargeCaution); + args.Cancel(); } + } - public void UpdateConnectionLights(ContainmentFieldGeneratorComponent component) + private void TurnOn(ContainmentFieldGeneratorComponent component) + { + component.Enabled = true; + ChangeFieldVisualizer(component); + _popupSystem.PopupEntity(Loc.GetString("comp-containment-turned-on"), component.Owner, Filter.Pvs(component.Owner)); + } + + private void TurnOff(ContainmentFieldGeneratorComponent component) + { + component.Enabled = false; + ChangeFieldVisualizer(component); + _popupSystem.PopupEntity(Loc.GetString("comp-containment-turned-off"), component.Owner, Filter.Pvs(component.Owner)); + } + + private void OnComponentRemoved(EntityUid uid, ContainmentFieldGeneratorComponent component, ComponentRemove args) + { + RemoveConnections(component); + } + + /// + /// Deletes the fields and removes the respective connections for the generators. + /// + private void RemoveConnections(ContainmentFieldGeneratorComponent component) + { + foreach (var (direction, value) in component.Connections) { - if (EntityManager.TryGetComponent(component.Owner, out var pointLightComponent)) + foreach (var field in value.Item2) { - bool hasAnyConnection = (component.Connection1 != null) || (component.Connection2 != null); - pointLightComponent.Enabled = hasAnyConnection; + QueueDel(field); + } + value.Item1.Connections.Remove(direction.GetOpposite()); + + if (value.Item1.Connections.Count == 0) //Change isconnected only if there's no more connections + { + value.Item1.IsConnected = false; + ChangeOnLightVisualizer(value.Item1); + } + + ChangeFieldVisualizer(value.Item1); + } + component.Connections.Clear(); + component.IsConnected = false; + ChangeOnLightVisualizer(component); + ChangeFieldVisualizer(component); + _popupSystem.PopupEntity(Loc.GetString("comp-containment-disconnected"), component.Owner, Filter.Pvs(component.Owner), PopupType.LargeCaution); + } + + #endregion + + #region Connections + + /// + /// Stores power in the generator. If it hits the threshold, it tries to establish a connection. + /// + /// The power that this generator received from the collision in + public void ReceivePower(int power, ContainmentFieldGeneratorComponent component) + { + component.PowerBuffer += power; + + var genXForm = Transform(component.Owner); + + if (component.PowerBuffer >= component.PowerMinimum) + { + var directions = Enum.GetValues().Length; + for (int i = 0; i < directions-1; i+=2) + { + var dir = (Direction)i; + + if (component.Connections.ContainsKey(dir)) + continue; // This direction already has an active connection + + TryGenerateFieldConnection(dir, component, genXForm); } } - public void RemoveConnection(ContainmentFieldConnection? connection, ContainmentFieldGeneratorComponent component) + ChangePowerVisualizer(power, component); + } + + public void LosePower(int power, ContainmentFieldGeneratorComponent component) + { + component.PowerBuffer -= power; + + if (component.PowerBuffer < component.PowerMinimum && component.Connections.Count != 0) { - if (component.Connection1?.Item2 == connection) - { - component.Connection1 = null; - component.IsConnected = false; - UpdateConnectionLights(component); - } - else if (component.Connection2?.Item2 == connection) - { - component.Connection2 = null; - component.IsConnected = false; - UpdateConnectionLights(component); - } - else if (connection != null) - { - Logger.Error("RemoveConnection called on Containmentfieldgenerator with a connection that can't be found in its connections."); - } + RemoveConnections(component); } - private bool TryGenerateFieldConnection([NotNullWhen(true)] ref Tuple? propertyFieldTuple, ContainmentFieldGeneratorComponent component) + ChangePowerVisualizer(power, component); + } + + /// + /// This will attempt to establish a connection of fields between two generators. + /// If all the checks pass and fields spawn, it will store this connection on each respective generator. + /// + /// The field generator establishes a connection in this direction. + /// The field generator component + /// The transform component for the first generator + /// + private bool TryGenerateFieldConnection(Direction dir, ContainmentFieldGeneratorComponent component, TransformComponent gen1XForm) + { + if (!component.Enabled) + return false; + + if (!gen1XForm.Anchored) + return false; + + var genWorldPosRot = gen1XForm.GetWorldPositionRotation(); + var dirRad = dir.ToAngle() + genWorldPosRot.WorldRotation; //needs to be like this for the raycast to work properly + + var ray = new CollisionRay(genWorldPosRot.WorldPosition, dirRad.ToVec(), component.CollisionMask); + var rayCastResults = _physics.IntersectRay(gen1XForm.MapID, ray, component.MaxLength, component.Owner, false); + var genQuery = GetEntityQuery(); + + RayCastResults? closestResult = null; + + foreach (var result in rayCastResults) { - if (propertyFieldTuple != null) return false; - if (!component.Enabled) return false; //don't gen a field unless it's on - if (EntityManager.TryGetComponent(component.Owner, out var xform) && !xform.Anchored) return false; + if (genQuery.HasComponent(result.HitEntity)) + closestResult = result; - foreach (var direction in new[] { Direction.North, Direction.East, Direction.South, Direction.West }) - { - if (component.Connection1?.Item1 == direction || component.Connection2?.Item1 == direction) continue; + break; + } + if (closestResult == null) + return false; - var dirVec = EntityManager.GetComponent(component.Owner).WorldRotation.RotateVec(direction.ToVec()); - var ray = new CollisionRay(EntityManager.GetComponent(component.Owner).WorldPosition, dirVec, (int) CollisionGroup.MobMask); - var rawRayCastResults = EntitySystem.Get().IntersectRay(EntityManager.GetComponent(component.Owner).MapID, ray, 4.5f, component.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 (!EntityManager.TryGetComponent(ent, out var fieldGeneratorComponent) || - fieldGeneratorComponent.Owner == component.Owner || - !HasFreeConnections(fieldGeneratorComponent) || - IsConnectedWith(component, fieldGeneratorComponent) || - !EntityManager.TryGetComponent(ent, out var collidableComponent) || - collidableComponent.BodyType != BodyType.Static) - { - continue; - } - - var connection = new ContainmentFieldConnection(component, 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"); - } - - component.IsConnected = true; - UpdateConnectionLights(component); - return true; - } + var ent = closestResult.Value.HitEntity; + if (!TryComp(ent, out var otherFieldGeneratorComponent) || + otherFieldGeneratorComponent == component || + !TryComp(ent, out var collidableComponent) || + collidableComponent.BodyType != BodyType.Static || + gen1XForm.ParentUid != Transform(otherFieldGeneratorComponent.Owner).ParentUid) + { return false; } - private bool IsConnectedWith(ContainmentFieldGeneratorComponent comp, ContainmentFieldGeneratorComponent otherComp) + var fields = GenerateFieldConnection(component, otherFieldGeneratorComponent); + + component.Connections[dir] = (otherFieldGeneratorComponent, fields); + otherFieldGeneratorComponent.Connections[dir.GetOpposite()] = (component, fields); + ChangeFieldVisualizer(otherFieldGeneratorComponent); + + if (!component.IsConnected) { - return otherComp == comp || comp.Connection1?.Item2.Generator1 == otherComp || comp.Connection1?.Item2.Generator2 == otherComp || - comp.Connection2?.Item2.Generator1 == otherComp || comp.Connection2?.Item2.Generator2 == otherComp; + component.IsConnected = true; + ChangeOnLightVisualizer(component); } - public void TryPowerConnection(ref Tuple? connectionProperty, ref int powerBuffer, int powerPerConnection, ContainmentFieldGeneratorComponent component) + if (!otherFieldGeneratorComponent.IsConnected) { - if (connectionProperty != null) - { - connectionProperty.Item2.SharedEnergyPool += powerPerConnection; - } - else - { - if (TryGenerateFieldConnection(ref connectionProperty, component)) - { - connectionProperty.Item2.SharedEnergyPool += powerPerConnection; - } - else - { - powerBuffer += powerPerConnection; - } - } + otherFieldGeneratorComponent.IsConnected = true; + ChangeOnLightVisualizer(otherFieldGeneratorComponent); } - public bool CanRepel(SharedSingularityComponent toRepel, ContainmentFieldGeneratorComponent component) => component.Connection1?.Item2?.CanRepel(toRepel) == true || - component.Connection2?.Item2?.CanRepel(toRepel) == true; - - public bool HasFreeConnections(ContainmentFieldGeneratorComponent component) - { - return component.Connection1 == null || component.Connection2 == null; - } - - + ChangeFieldVisualizer(component); + UpdateConnectionLights(component); + _popupSystem.PopupEntity(Loc.GetString("comp-containment-connected"), component.Owner, Filter.Pvs(component.Owner)); + return true; } + + /// + /// Spawns fields between two generators if the finds two generators to connect. + /// + /// The source field generator + /// The second generator that the source is connected to + /// + private List GenerateFieldConnection(ContainmentFieldGeneratorComponent firstGenComp, ContainmentFieldGeneratorComponent secondGenComp) + { + var fieldList = new List(); + var gen1Coords = Transform(firstGenComp.Owner).Coordinates; + var gen2Coords = Transform(secondGenComp.Owner).Coordinates; + + var delta = (gen2Coords - gen1Coords).Position; + var dirVec = delta.Normalized; + var stopDist = delta.Length; + var currentOffset = dirVec; + while (currentOffset.Length < stopDist) + { + var currentCoords = gen1Coords.Offset(currentOffset); + var newField = Spawn(firstGenComp.CreatedField, currentCoords); + + var fieldXForm = Transform(newField); + fieldXForm.AttachParent(firstGenComp.Owner); + if (dirVec.GetDir() == Direction.East || dirVec.GetDir() == Direction.West) + { + var angle = fieldXForm.LocalPosition.ToAngle(); + var rotateBy90 = angle.Degrees + 90; + var rotatedAngle = Angle.FromDegrees(rotateBy90); + + fieldXForm.LocalRotation = rotatedAngle; + } + + fieldList.Add(newField); + currentOffset += dirVec; + } + return fieldList; + } + + /// + /// Creates a light component for the spawned fields. + /// + public void UpdateConnectionLights(ContainmentFieldGeneratorComponent component) + { + if (EntityManager.TryGetComponent(component.Owner, out var pointLightComponent)) + { + bool hasAnyConnection = component.Connections != null; + pointLightComponent.Enabled = hasAnyConnection; + } + } + + /// + /// Checks to see if this or the other gens connected to a new grid. If they did, remove connection. + /// + public void GridCheck(ContainmentFieldGeneratorComponent component) + { + var xFormQuery = GetEntityQuery(); + + foreach (var (_, generators) in component.Connections) + { + var gen1ParentGrid = xFormQuery.GetComponent(component.Owner).ParentUid; + var gent2ParentGrid = xFormQuery.GetComponent(generators.Item1.Owner).ParentUid; + + if (gen1ParentGrid != gent2ParentGrid) + RemoveConnections(component); + } + } + + #endregion + + #region VisualizerHelpers + /// + /// Check if a fields power falls between certain ranges to update the field gen visual for power. + /// + /// + /// + private void ChangePowerVisualizer(int power, ContainmentFieldGeneratorComponent component) + { + if (!TryComp(component.Owner, out var appearance)) + return; + + if(component.PowerBuffer == 0) + appearance.SetData(ContainmentFieldGeneratorVisuals.PowerLight, PowerLevelVisuals.NoPower); + + if (component.PowerBuffer > 0 && component.PowerBuffer < component.PowerMinimum) + appearance.SetData(ContainmentFieldGeneratorVisuals.PowerLight, PowerLevelVisuals.LowPower); + + if (component.PowerBuffer >= component.PowerMinimum && component.PowerBuffer < 25) + { + appearance.SetData(ContainmentFieldGeneratorVisuals.PowerLight, PowerLevelVisuals.MediumPower); + } + + if (component.PowerBuffer == 25) + { + appearance.SetData(ContainmentFieldGeneratorVisuals.PowerLight, PowerLevelVisuals.HighPower); + } + } + + /// + /// Check if a field has any or no connections and if it's enabled to toggle the field level light + /// + /// + private void ChangeFieldVisualizer(ContainmentFieldGeneratorComponent component) + { + if (!TryComp(component.Owner, out var appearance)) + return; + + if (component.Connections.Count == 0 && !component.Enabled) + { + appearance.SetData(ContainmentFieldGeneratorVisuals.FieldLight, FieldLevelVisuals.NoLevel); + } + + if (component.Connections.Count == 0 && component.Enabled) + { + appearance.SetData(ContainmentFieldGeneratorVisuals.FieldLight, FieldLevelVisuals.On); + } + + if (component.Connections.Count == 1) + { + appearance.SetData(ContainmentFieldGeneratorVisuals.FieldLight, FieldLevelVisuals.OneField); + } + + if (component.Connections.Count > 1) + { + appearance.SetData(ContainmentFieldGeneratorVisuals.FieldLight, FieldLevelVisuals.MultipleFields); + } + } + + private void ChangeOnLightVisualizer(ContainmentFieldGeneratorComponent component) + { + if (!TryComp(component.Owner, out var appearance)) + return; + + appearance.SetData(ContainmentFieldGeneratorVisuals.OnLight, component.IsConnected); + } + #endregion } diff --git a/Content.Server/Singularity/EntitySystems/ContainmentFieldSystem.cs b/Content.Server/Singularity/EntitySystems/ContainmentFieldSystem.cs new file mode 100644 index 0000000000..d2ea75ce42 --- /dev/null +++ b/Content.Server/Singularity/EntitySystems/ContainmentFieldSystem.cs @@ -0,0 +1,43 @@ +using Content.Server.Popups; +using Content.Server.Shuttles.Components; +using Content.Server.Singularity.Components; +using Content.Shared.Popups; +using Content.Shared.Tag; +using Content.Shared.Throwing; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Player; + +namespace Content.Server.Singularity.EntitySystems; + +public sealed class ContainmentFieldSystem : EntitySystem +{ + [Dependency] private readonly ThrowingSystem _throwing = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(HandleFieldCollide); + } + + private void HandleFieldCollide(EntityUid uid, ContainmentFieldComponent component, StartCollideEvent args) + { + var otherBody = args.OtherFixture.Body.Owner; + + if (TryComp(otherBody, out var garbage)) + { + _popupSystem.PopupEntity(Loc.GetString("comp-field-vaporized", ("entity", otherBody)), component.Owner, Filter.Pvs(component.Owner), PopupType.LargeCaution); + QueueDel(garbage.Owner); + } + + if (TryComp(otherBody, out var physics) && physics.Mass <= component.MaxMass && physics.Hard) + { + var fieldDir = Transform(component.Owner).WorldPosition; + var playerDir = Transform(otherBody).WorldPosition; + + _throwing.TryThrow(otherBody, playerDir-fieldDir, strength: component.ThrowForce); + } + } +} diff --git a/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs b/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs new file mode 100644 index 0000000000..aa58a4a3a6 --- /dev/null +++ b/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs @@ -0,0 +1,33 @@ +using Content.Server.ParticleAccelerator.Components; +using Content.Server.Singularity.Components; +using Content.Shared.Singularity.Components; +using Robust.Shared.Physics.Dynamics; + +namespace Content.Server.Singularity.EntitySystems; + +public sealed class SingularityGeneratorSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(HandleParticleCollide); + } + + private void HandleParticleCollide(EntityUid uid, ParticleProjectileComponent component, StartCollideEvent args) + { + if (EntityManager.TryGetComponent(args.OtherFixture.Body.Owner, out var singularityGeneratorComponent)) + { + singularityGeneratorComponent.Power += component.State switch + { + ParticleAcceleratorPowerState.Standby => 0, + ParticleAcceleratorPowerState.Level0 => 1, + ParticleAcceleratorPowerState.Level1 => 2, + ParticleAcceleratorPowerState.Level2 => 4, + ParticleAcceleratorPowerState.Level3 => 8, + _ => 0 + }; + EntityManager.QueueDeleteEntity(uid); + } + } +} diff --git a/Content.Server/Singularity/EntitySystems/SingularitySystem.cs b/Content.Server/Singularity/EntitySystems/SingularitySystem.cs index b878c8f10c..85ebd76dba 100644 --- a/Content.Server/Singularity/EntitySystems/SingularitySystem.cs +++ b/Content.Server/Singularity/EntitySystems/SingularitySystem.cs @@ -132,7 +132,7 @@ namespace Content.Server.Singularity.EntitySystems !EntityManager.HasComponent(entity) && (component.Level > 4 || !EntityManager.HasComponent(entity) && - !(EntityManager.TryGetComponent(entity, out var containFieldGen) && _fieldGeneratorSystem.CanRepel(component, containFieldGen))); + !EntityManager.HasComponent(entity)); } private void HandleDestroy(ServerSingularityComponent component, EntityUid entity) diff --git a/Content.Shared/Singularity/Components/SharedContainmentFieldComponent.cs b/Content.Shared/Singularity/Components/SharedContainmentFieldComponent.cs index e8d56d087a..3bf970f501 100644 --- a/Content.Shared/Singularity/Components/SharedContainmentFieldComponent.cs +++ b/Content.Shared/Singularity/Components/SharedContainmentFieldComponent.cs @@ -1,6 +1,2 @@ -namespace Content.Shared.Singularity.Components -{ - public abstract class SharedContainmentFieldComponent : Component - { - } -} +namespace Content.Shared.Singularity.Components; +public abstract class SharedContainmentFieldComponent : Component { } diff --git a/Content.Shared/Singularity/Components/SharedContainmentFieldGeneratorComponent.cs b/Content.Shared/Singularity/Components/SharedContainmentFieldGeneratorComponent.cs index 1764e1f78e..4a3aaab505 100644 --- a/Content.Shared/Singularity/Components/SharedContainmentFieldGeneratorComponent.cs +++ b/Content.Shared/Singularity/Components/SharedContainmentFieldGeneratorComponent.cs @@ -1,6 +1,30 @@ -namespace Content.Shared.Singularity.Components +using Robust.Shared.Serialization; + +namespace Content.Shared.Singularity.Components; +public abstract class SharedContainmentFieldGeneratorComponent : Component { } + +[Serializable, NetSerializable] +public enum ContainmentFieldGeneratorVisuals : byte { - public abstract class SharedContainmentFieldGeneratorComponent : Component - { - } + PowerLight, + FieldLight, + OnLight, +} + +[Serializable, NetSerializable] +public enum PowerLevelVisuals : byte +{ + NoPower, + LowPower, + MediumPower, + HighPower, +} + +[Serializable, NetSerializable] +public enum FieldLevelVisuals : byte +{ + NoLevel, + On, + OneField, + MultipleFields, } diff --git a/Resources/Locale/en-US/singularity/components/containment-component.ftl b/Resources/Locale/en-US/singularity/components/containment-component.ftl index 852981c05c..e0e7d4c039 100644 --- a/Resources/Locale/en-US/singularity/components/containment-component.ftl +++ b/Resources/Locale/en-US/singularity/components/containment-component.ftl @@ -1,7 +1,13 @@ comp-containment-turned-on = The containment field generator boots up. comp-containment-turned-off = The containment field generator shuts down. +comp-containment-on = It's switched [color=green]on[/color], ready to generate a connection. +comp-containment-off = It's switched [color=red]off[/color]. + comp-containment-connected = The containment field generator shoots out a light as it establishes a connection! comp-containment-disconnected = The containment field generator light fades away, severing the connection. comp-containment-anchor-warning = You cannot unanchor the containment field generator while it's on or connected! +comp-containment-toggle-warning = You cannot turn the generator off while it's connected. + +comp-field-vaporized = The {$entity} hits the field and vaporizes into nothing! diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index f78f7d215c..b9dfa10b2a 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -41718,22 +41718,6 @@ entities: - pos: 14.5,-27.5 parent: 852 type: Transform -- uid: 4334 - type: Emitter - components: - - pos: 71.5,9.5 - parent: 852 - type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - uid: 4335 type: CableMV components: @@ -41801,24 +41785,6 @@ entities: pos: 63.5,2.5 parent: 852 type: Transform -- uid: 4345 - type: RadiationCollector - components: - - pos: 78.5,3.5 - parent: 852 - type: Transform -- uid: 4346 - type: RadiationCollector - components: - - pos: 78.5,5.5 - parent: 852 - type: Transform -- uid: 4347 - type: RadiationCollector - components: - - pos: 74.5,9.5 - parent: 852 - type: Transform - uid: 4348 type: CableMV components: @@ -41897,22 +41863,6 @@ entities: - pos: 77.5,9.5 parent: 852 type: Transform -- uid: 4361 - type: Emitter - components: - - pos: 75.5,9.5 - parent: 852 - type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - uid: 4362 type: CableMV components: @@ -41960,23 +41910,6 @@ entities: - pos: 78.5,9.5 parent: 852 type: Transform -- uid: 4368 - type: Emitter - components: - - rot: -1.5707963267948966 rad - pos: 78.5,2.5 - parent: 852 - type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - uid: 4369 type: CableMV components: @@ -42386,23 +42319,6 @@ entities: - pos: 80.5,-6.5 parent: 852 type: Transform -- uid: 4433 - type: Emitter - components: - - rot: 1.5707963267948966 rad - pos: 64.5,2.5 - parent: 852 - type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - uid: 4434 type: Grille components: @@ -43245,18 +43161,6 @@ entities: machine_parts: !type:Container ents: [] type: ContainerContainer -- uid: 4549 - type: RadiationCollector - components: - - pos: 68.5,9.5 - parent: 852 - type: Transform -- uid: 4550 - type: RadiationCollector - components: - - pos: 69.5,9.5 - parent: 852 - type: Transform - uid: 4551 type: Grille components: @@ -43275,29 +43179,6 @@ entities: - pos: 78.5,-4.5 parent: 852 type: Transform -- uid: 4554 - type: Emitter - components: - - rot: 3.141592653589793 rad - pos: 67.5,-4.5 - parent: 852 - type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer -- uid: 4555 - type: RadiationCollector - components: - - pos: 70.5,9.5 - parent: 852 - type: Transform - uid: 4556 type: Emitter components: @@ -43315,18 +43196,6 @@ entities: machine_parts: !type:Container ents: [] type: ContainerContainer -- uid: 4557 - type: RadiationCollector - components: - - pos: 72.5,9.5 - parent: 852 - type: Transform -- uid: 4558 - type: RadiationCollector - components: - - pos: 73.5,9.5 - parent: 852 - type: Transform - uid: 4559 type: ContainmentFieldGenerator components: @@ -43409,46 +43278,6 @@ entities: pos: 56.5,2.5 parent: 852 type: Transform -- uid: 4572 - type: RadiationCollector - components: - - pos: 74.5,-4.5 - parent: 852 - type: Transform -- uid: 4573 - type: RadiationCollector - components: - - pos: 72.5,-4.5 - parent: 852 - type: Transform -- uid: 4574 - type: RadiationCollector - components: - - pos: 69.5,-4.5 - parent: 852 - type: Transform -- uid: 4575 - type: Emitter - components: - - pos: 67.5,9.5 - parent: 852 - type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer -- uid: 4576 - type: ContainmentFieldGenerator - components: - - pos: 67.5,2.5 - parent: 852 - type: Transform - uid: 4577 type: ReinforcedWindow components: @@ -45870,12 +45699,6 @@ entities: - containers: ItemCabinet: !type:ContainerSlot {} type: ContainerContainer -- uid: 4910 - type: RadiationCollector - components: - - pos: 78.5,4.5 - parent: 852 - type: Transform - uid: 4911 type: CableMV components: @@ -57150,12 +56973,6 @@ entities: - pos: 56.5,2.5 parent: 852 type: Transform -- uid: 6298 - type: ContainmentFieldGenerator - components: - - pos: 71.5,-1.5 - parent: 852 - type: Transform - uid: 6299 type: ContainmentFieldGenerator components: @@ -57181,12 +56998,6 @@ entities: - pos: 44.5,1.5 parent: 852 type: Transform -- uid: 6303 - type: RadiationCollector - components: - - pos: 73.5,-4.5 - parent: 852 - type: Transform - uid: 6304 type: CableHV components: @@ -57199,24 +57010,6 @@ entities: - pos: 47.5,1.5 parent: 852 type: Transform -- uid: 6306 - type: ContainmentFieldGenerator - components: - - pos: 71.5,6.5 - parent: 852 - type: Transform -- uid: 6307 - type: RadiationCollector - components: - - pos: 68.5,-4.5 - parent: 852 - type: Transform -- uid: 6308 - type: RadiationCollector - components: - - pos: 70.5,-4.5 - parent: 852 - type: Transform - uid: 6309 type: AirlockEngineeringGlassLocked components: @@ -57255,29 +57048,6 @@ entities: - pos: 50.5,1.5 parent: 852 type: Transform -- uid: 6315 - type: ContainmentFieldGenerator - components: - - pos: 75.5,2.5 - parent: 852 - type: Transform -- uid: 6316 - type: Emitter - components: - - rot: 3.141592653589793 rad - pos: 75.5,-4.5 - parent: 852 - type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - uid: 6317 type: CableMV components: @@ -57308,35 +57078,6 @@ entities: - pos: 75.5,6.5 parent: 852 type: Transform -- uid: 6322 - type: RadiationCollector - components: - - pos: 78.5,0.5 - parent: 852 - type: Transform -- uid: 6323 - type: RadiationCollector - components: - - pos: 78.5,1.5 - parent: 852 - type: Transform -- uid: 6324 - type: Emitter - components: - - rot: 3.141592653589793 rad - pos: 71.5,-4.5 - parent: 852 - type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - uid: 6325 type: CableMV components: @@ -63171,12 +62912,6 @@ entities: - pos: 43.5,6.5 parent: 852 type: Transform -- uid: 7175 - type: RadiationCollector - components: - - pos: 78.5,-0.5 - parent: 852 - type: Transform - uid: 7176 type: GrilleBroken components: diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/containment.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/containment.yml index 2b6aa5630d..5681fe18ec 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/containment.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/containment.yml @@ -21,6 +21,20 @@ state: icon netsync: false noRot: true + layers: + - state: icon + - state: p1 + map: ["powerLight"] + visible: false + shader: unshaded + - state: a1 + map: ["fieldLight"] + visible: false + shader: unshaded + - state: on + map: ["connectedLight"] + visible: false + shader: unshaded - type: Icon sprite: Structures/Power/Generation/Singularity/containment.rsi state: icon @@ -31,6 +45,25 @@ radius: 32 energy: 2.0 softness: 32.0 + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ContainmentFieldGeneratorVisuals.PowerLight: + powerLight: + NoPower: {visible: false} + LowPower: {visible: true, state: p1} + MediumPower: {visible: true, state: p3} + HighPower: {visible: true, state: p6} + enum.ContainmentFieldGeneratorVisuals.FieldLight: + fieldLight: + NoLevel: {visible: false} + On: {visible: true, state: a1} + OneField: {visible: true, state: a2} + MultipleFields: {visible: true, state: a3} + enum.ContainmentFieldGeneratorVisuals.OnLight: + connectedLight: + True: { visible: true } + False: { visible: false } - type: entity id: ContainmentField @@ -52,12 +85,11 @@ - FullTileMask layer: - GlassLayer - - type: Transform - anchored: true - type: Sprite sprite: Structures/Power/Generation/Singularity/containment_field.rsi state: field netsync: false + noRot: true - type: Icon sprite: Structures/Power/Generation/Singularity/containment_field.rsi state: field