From 4c7670fe82a849311102a7c32b549600f5dfce7a Mon Sep 17 00:00:00 2001 From: Paul Ritter Date: Fri, 10 Sep 2021 09:26:05 +0200 Subject: [PATCH] conveyor & recycler ecs (#4537) * conveyor done, recycler still todo * done, just need to suss out the physics part. pinged sloth about it * ship it --- .../Visualizers/ConveyorVisualizer.cs | 19 +-- .../Recycling/RecyclerVisualizer.cs | 39 ++++-- Content.Server/Conveyor/ConveyorComponent.cs | 132 +----------------- Content.Server/Conveyor/ConveyorSystem.cs | 124 +++++++++++++++- .../Physics/Controllers/ConveyorController.cs | 93 +++--------- .../Recycling/Components/RecyclerComponent.cs | 61 +------- Content.Server/Recycling/RecyclerSystem.cs | 15 +- .../Entities/Structures/Machines/recycler.yml | 14 +- .../Entities/Structures/conveyor.yml | 11 +- 9 files changed, 208 insertions(+), 300 deletions(-) diff --git a/Content.Client/Conveyor/Visualizers/ConveyorVisualizer.cs b/Content.Client/Conveyor/Visualizers/ConveyorVisualizer.cs index aabad00639..04ffc65b7f 100644 --- a/Content.Client/Conveyor/Visualizers/ConveyorVisualizer.cs +++ b/Content.Client/Conveyor/Visualizers/ConveyorVisualizer.cs @@ -26,17 +26,18 @@ namespace Content.Client.Conveyor.Visualizers return; } - appearance.TryGetData(ConveyorVisuals.State, out ConveyorState state); - - var texture = state switch + if (appearance.TryGetData(ConveyorVisuals.State, out ConveyorState state)) { - ConveyorState.Off => _stateStopped, - ConveyorState.Forward => _stateRunning, - ConveyorState.Reversed => _stateReversed, - _ => throw new ArgumentOutOfRangeException() - }; + var texture = state switch + { + ConveyorState.Off => _stateStopped, + ConveyorState.Forward => _stateRunning, + ConveyorState.Reversed => _stateReversed, + _ => throw new ArgumentOutOfRangeException() + }; - sprite.LayerSetState(0, texture); + sprite.LayerSetState(0, texture); + } } public override void InitializeEntity(IEntity entity) diff --git a/Content.Client/Recycling/RecyclerVisualizer.cs b/Content.Client/Recycling/RecyclerVisualizer.cs index f831372b4c..d7a3974a15 100644 --- a/Content.Client/Recycling/RecyclerVisualizer.cs +++ b/Content.Client/Recycling/RecyclerVisualizer.cs @@ -1,4 +1,5 @@ -using Content.Shared.Recycling; +using Content.Shared.Conveyor; +using Content.Shared.Recycling; using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; @@ -9,11 +10,11 @@ namespace Content.Client.Recycling [UsedImplicitly] public class RecyclerVisualizer : AppearanceVisualizer { - [DataField("state_clean")] - private string? _stateClean; + [DataField("state_on")] + private string _stateOn = "grinder-o1"; - [DataField("state_bloody")] - private string? _stateBloody; + [DataField("state_off")] + private string _stateOff = "grinder-o0"; public override void InitializeEntity(IEntity entity) { @@ -25,10 +26,7 @@ namespace Content.Client.Recycling return; } - appearance.TryGetData(RecyclerVisuals.Bloody, out bool bloody); - sprite.LayerSetState(RecyclerVisualLayers.Bloody, bloody - ? _stateBloody - : _stateClean); + UpdateAppearance(appearance, sprite); } public override void OnChangeData(AppearanceComponent component) @@ -40,15 +38,28 @@ namespace Content.Client.Recycling return; } - component.TryGetData(RecyclerVisuals.Bloody, out bool bloody); - sprite.LayerSetState(RecyclerVisualLayers.Bloody, bloody - ? _stateBloody - : _stateClean); + UpdateAppearance(component, sprite); + } + + private void UpdateAppearance(AppearanceComponent component, ISpriteComponent sprite) + { + var state = _stateOff; + if (component.TryGetData(ConveyorVisuals.State, out ConveyorState conveyorState) && conveyorState != ConveyorState.Off) + { + state = _stateOn; + } + + if (component.TryGetData(RecyclerVisuals.Bloody, out bool bloody) && bloody) + { + state += "bld"; + } + + sprite.LayerSetState(RecyclerVisualLayers.Main, state); } } public enum RecyclerVisualLayers : byte { - Bloody + Main } } diff --git a/Content.Server/Conveyor/ConveyorComponent.cs b/Content.Server/Conveyor/ConveyorComponent.cs index 175f9faa2f..909a91d6fb 100644 --- a/Content.Server/Conveyor/ConveyorComponent.cs +++ b/Content.Server/Conveyor/ConveyorComponent.cs @@ -1,9 +1,11 @@ +using System.Collections.Generic; using Content.Server.Items; using Content.Server.MachineLinking.Components; using Content.Server.Power.Components; using Content.Shared.Conveyor; using Content.Shared.MachineLinking; using Robust.Server.GameObjects; +using Robust.Shared.Analyzers; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Maths; @@ -14,151 +16,29 @@ using Robust.Shared.ViewVariables; namespace Content.Server.Conveyor { [RegisterComponent] + [Friend(typeof(ConveyorSystem))] public class ConveyorComponent : Component { public override string Name => "Conveyor"; - [ViewVariables] private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered; - /// /// The angle to move entities by in relation to the owner's rotation. /// [ViewVariables(VVAccess.ReadWrite)] [DataField("angle")] - private Angle _angle = Angle.Zero; - - public float Speed => _speed; + public Angle Angle = Angle.Zero; /// /// The amount of units to move the entity by per second. /// [ViewVariables(VVAccess.ReadWrite)] [DataField("speed")] - private float _speed = 2f; + public float Speed = 2f; - private ConveyorState _state; /// /// The current state of this conveyor /// [ViewVariables(VVAccess.ReadWrite)] - private ConveyorState State - { - get => _state; - set - { - _state = value; - UpdateAppearance(); - } - } - - public override void HandleMessage(ComponentMessage message, IComponent? component) - { - base.HandleMessage(message, component); - switch (message) - { - case PowerChangedMessage powerChanged: - OnPowerChanged(powerChanged); - break; - } - } - - private void OnPowerChanged(PowerChangedMessage e) - { - UpdateAppearance(); - } - - private void UpdateAppearance() - { - if (Owner.TryGetComponent(out var appearance)) - { - if (Powered) - { - appearance.SetData(ConveyorVisuals.State, _state); - } - else - { - appearance.SetData(ConveyorVisuals.State, ConveyorState.Off); - } - } - } - - /// - /// Calculates the angle in which entities on top of this conveyor - /// belt are pushed in - /// - /// - /// The angle when taking into account if the conveyor is reversed - /// - public Angle GetAngle() - { - var adjustment = _state == ConveyorState.Reversed ? MathHelper.Pi : 0; - var radians = MathHelper.DegreesToRadians(_angle); - - return new Angle(Owner.Transform.LocalRotation.Theta + radians + adjustment); - } - - public bool CanRun() - { - if (State == ConveyorState.Off) - { - return false; - } - - if (Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) && - !receiver.Powered) - { - return false; - } - - if (Owner.HasComponent()) - { - return false; - } - - return true; - } - - public bool CanMove(IEntity entity) - { - // TODO We should only check status InAir or Static or MapGrid or /mayber/ container - if (entity == Owner) - { - return false; - } - - if (!entity.TryGetComponent(out IPhysBody? physics) || - physics.BodyType == BodyType.Static) - { - return false; - } - - if (entity.HasComponent()) - { - return false; - } - - if (entity.HasComponent()) - { - return false; - } - - if (entity.IsInContainer()) - { - return false; - } - - return true; - } - - public void SetState(TwoWayLeverSignal signal) - { - State = signal switch - { - TwoWayLeverSignal.Left => ConveyorState.Reversed, - TwoWayLeverSignal.Middle => ConveyorState.Off, - TwoWayLeverSignal.Right => ConveyorState.Forward, - _ => ConveyorState.Off - }; - } + public ConveyorState State; } } diff --git a/Content.Server/Conveyor/ConveyorSystem.cs b/Content.Server/Conveyor/ConveyorSystem.cs index c416b77f5d..8edda86258 100644 --- a/Content.Server/Conveyor/ConveyorSystem.cs +++ b/Content.Server/Conveyor/ConveyorSystem.cs @@ -1,15 +1,29 @@ -using Content.Server.MachineLinking.Events; +using System.Collections.Generic; +using Content.Server.Items; +using Content.Server.MachineLinking.Events; using Content.Server.MachineLinking.Models; +using Content.Server.Power.Components; +using Content.Server.Recycling.Components; using Content.Server.Stunnable.Components; +using Content.Shared.Conveyor; using Content.Shared.MachineLinking; +using Content.Shared.Movement.Components; using Content.Shared.Notification.Managers; +using Robust.Server.GameObjects; +using Robust.Shared.Containers; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Dynamics; namespace Content.Server.Conveyor { public class ConveyorSystem : EntitySystem { + [Dependency] private IEntityLookup _entityLookup = default!; + public override void Initialize() { base.Initialize(); @@ -17,6 +31,27 @@ namespace Content.Server.Conveyor SubscribeLocalEvent(OnSignalReceived); SubscribeLocalEvent(OnPortDisconnected); SubscribeLocalEvent(OnLinkAttempt); + SubscribeLocalEvent(OnPowerChanged); + } + + private void OnPowerChanged(EntityUid uid, ConveyorComponent component, PowerChangedEvent args) + { + UpdateAppearance(component); + } + + private void UpdateAppearance(ConveyorComponent component) + { + if (component.Owner.TryGetComponent(out var appearance)) + { + if (component.Owner.TryGetComponent(out var receiver) && receiver.Powered) + { + appearance.SetData(ConveyorVisuals.State, component.State); + } + else + { + appearance.SetData(ConveyorVisuals.State, ConveyorState.Off); + } + } } private void OnLinkAttempt(EntityUid uid, ConveyorComponent component, LinkAttemptEvent args) @@ -35,7 +70,7 @@ namespace Content.Server.Conveyor private void OnPortDisconnected(EntityUid uid, ConveyorComponent component, PortDisconnectedEvent args) { - component.SetState(TwoWayLeverSignal.Middle); + SetState(component, TwoWayLeverSignal.Middle); } private void OnSignalReceived(EntityUid uid, ConveyorComponent component, SignalReceivedEvent args) @@ -43,9 +78,92 @@ namespace Content.Server.Conveyor switch (args.Port) { case "state": - component.SetState((TwoWayLeverSignal) args.Value!); + SetState(component, (TwoWayLeverSignal) args.Value!); break; } } + + private void SetState(ConveyorComponent component, TwoWayLeverSignal signal) + { + component.State = signal switch + { + TwoWayLeverSignal.Left => ConveyorState.Reversed, + TwoWayLeverSignal.Middle => ConveyorState.Off, + TwoWayLeverSignal.Right => ConveyorState.Forward, + _ => ConveyorState.Off + }; + UpdateAppearance(component); + } + + public bool CanRun(ConveyorComponent component) + { + if (component.State == ConveyorState.Off) + { + return false; + } + + if (component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) && + !receiver.Powered) + { + return false; + } + + if (component.Owner.HasComponent()) + { + return false; + } + + return true; + } + + /// + /// Calculates the angle in which entities on top of this conveyor + /// belt are pushed in + /// + /// + /// The angle when taking into account if the conveyor is reversed + /// + public Angle GetAngle(ConveyorComponent component) + { + var adjustment = component.State == ConveyorState.Reversed ? MathHelper.Pi/2 : -MathHelper.Pi/2; + var radians = MathHelper.DegreesToRadians(component.Angle); + + return new Angle(component.Owner.Transform.LocalRotation.Theta + radians + adjustment); + } + + public IEnumerable<(IEntity, IPhysBody)> GetEntitiesToMove(ConveyorComponent comp) + { + //todo uuuhhh cache this + foreach (var entity in _entityLookup.GetEntitiesIntersecting(comp.Owner, LookupFlags.Approximate)) + { + if (entity.Deleted) + { + continue; + } + + if (entity == comp.Owner) + { + continue; + } + + if (!entity.TryGetComponent(out IPhysBody? physics) || + physics.BodyType == BodyType.Static || physics.BodyStatus == BodyStatus.InAir || entity.IsWeightless()) + { + continue; + } + + if (entity.HasComponent()) + { + continue; + } + + if (entity.IsInContainer()) + { + continue; + } + + yield return (entity, physics); + } + } } } diff --git a/Content.Server/Physics/Controllers/ConveyorController.cs b/Content.Server/Physics/Controllers/ConveyorController.cs index bfbd261a25..4322d279a0 100644 --- a/Content.Server/Physics/Controllers/ConveyorController.cs +++ b/Content.Server/Physics/Controllers/ConveyorController.cs @@ -18,102 +18,43 @@ namespace Content.Server.Physics.Controllers public override void UpdateBeforeSolve(bool prediction, float frameTime) { base.UpdateBeforeSolve(prediction, frameTime); + var system = EntitySystem.Get(); foreach (var comp in ComponentManager.EntityQuery()) { - Convey(comp, frameTime); - } - - // TODO: Uhh you can probably wrap the recycler's conveying properties into... conveyor - foreach (var comp in ComponentManager.EntityQuery()) - { - ConveyRecycler(comp, frameTime); + Convey(system, comp, frameTime); } } - private void Convey(ConveyorComponent comp, float frameTime) + private void Convey(ConveyorSystem system, ConveyorComponent comp, float frameTime) { - // TODO: Use ICollideBehavior and cache intersecting // Use an event for conveyors to know what needs to run - if (!comp.CanRun()) + if (!system.CanRun(comp)) { return; } - var intersecting = IoCManager.Resolve().GetEntitiesIntersecting(comp.Owner, LookupFlags.Approximate | LookupFlags.IncludeAnchored); - var direction = comp.GetAngle().ToVec(); - Vector2? ownerPos = null; + var direction = system.GetAngle(comp).ToVec(); + var ownerPos = comp.Owner.Transform.WorldPosition; - foreach (var entity in intersecting) + foreach (var (entity, physics) in EntitySystem.Get().GetEntitiesToMove(comp)) { - if (!comp.CanMove(entity)) continue; - - if (!entity.TryGetComponent(out IPhysBody? physics) || physics.BodyStatus == BodyStatus.InAir || - entity.IsWeightless()) continue; - - ownerPos ??= comp.Owner.Transform.WorldPosition; - var itemRelativeToConveyor = entity.Transform.WorldPosition - ownerPos.Value; - - physics.LinearVelocity += Convey(direction * comp.Speed, frameTime, itemRelativeToConveyor); + var itemRelativeToConveyor = entity.Transform.WorldPosition - ownerPos; + physics.LinearVelocity += Convey(direction, comp.Speed, frameTime, itemRelativeToConveyor); } } - // TODO Uhhh I did a shit job plz fix smug - private Vector2 Convey(Vector2 velocityDirection, float frameTime, Vector2 itemRelativeToConveyor) + private Vector2 Convey(Vector2 direction, float speed, float frameTime, Vector2 itemRelativeToConveyor) { - //gravitating item towards center - //http://csharphelper.com/blog/2016/09/find-the-shortest-distance-between-a-point-and-a-line-segment-in-c/ - Vector2 centerPoint; + if(speed == 0 || direction.Length == 0) return Vector2.Zero; + direction = direction.Normalized; - var t = 0f; - if (velocityDirection.Length > 0) // if velocitydirection is 0, this calculation will divide by 0 - { - t = Vector2.Dot(itemRelativeToConveyor, velocityDirection) / - Vector2.Dot(velocityDirection, velocityDirection); - } + var dirNormal = new Vector2(direction.Y, direction.X); + var dot = Vector2.Dot(itemRelativeToConveyor, dirNormal); - if (t < 0) - { - centerPoint = new Vector2(); - } - else if (t > 1) - { - centerPoint = velocityDirection; - } - else - { - centerPoint = velocityDirection * t; - } + var velocity = direction * speed * 5; + velocity += dirNormal * speed * -dot; - var delta = centerPoint - itemRelativeToConveyor; - return delta * (400 * delta.Length) * frameTime; - } - - private void ConveyRecycler(RecyclerComponent comp, float frameTime) - { - if (!comp.CanRun()) - { - comp.Intersecting.Clear(); - return; - } - - var direction = Vector2.UnitX; - Vector2? ownerPos = null; - - // TODO: I know it sucks but conveyors need a refactor - for (var i = comp.Intersecting.Count - 1; i >= 0; i--) - { - var entity = comp.Intersecting[i]; - - if (entity.Deleted || !comp.CanMove(entity) || !IoCManager.Resolve().IsIntersecting(comp.Owner, entity)) - { - comp.Intersecting.RemoveAt(i); - continue; - } - - if (!entity.TryGetComponent(out IPhysBody? physics)) continue; - ownerPos ??= comp.Owner.Transform.WorldPosition; - physics.LinearVelocity += Convey(direction, frameTime, entity.Transform.WorldPosition - ownerPos.Value); - } + return velocity * frameTime; } } } diff --git a/Content.Server/Recycling/Components/RecyclerComponent.cs b/Content.Server/Recycling/Components/RecyclerComponent.cs index 1b321641e6..ea7b4f1de4 100644 --- a/Content.Server/Recycling/Components/RecyclerComponent.cs +++ b/Content.Server/Recycling/Components/RecyclerComponent.cs @@ -13,6 +13,7 @@ using Content.Shared.Notification.Managers; using Content.Shared.Recycling; using Robust.Server.GameObjects; using Robust.Server.Player; +using Robust.Shared.Analyzers; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -27,28 +28,25 @@ namespace Content.Server.Recycling.Components { // TODO: Add sound and safe beep [RegisterComponent] + [Friend(typeof(RecyclerSystem))] public class RecyclerComponent : Component, ISuicideAct { public override string Name => "Recycler"; - public List Intersecting { get; set; } = new(); - /// /// Whether or not sentient beings will be recycled /// - [ViewVariables(VVAccess.ReadWrite)] [DataField("safe")] + [ViewVariables(VVAccess.ReadWrite)] + [DataField("safe")] internal bool Safe = true; /// /// The percentage of material that will be recovered /// - [ViewVariables(VVAccess.ReadWrite)] [DataField("efficiency")] + [ViewVariables(VVAccess.ReadWrite)] + [DataField("efficiency")] internal float Efficiency = 0.25f; - internal bool Powered => - !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || - receiver.Powered; - private void Clean() { if (Owner.TryGetComponent(out AppearanceComponent? appearance)) @@ -57,53 +55,6 @@ namespace Content.Server.Recycling.Components } } - public bool CanRun() - { - if (Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) && - !receiver.Powered) - { - return false; - } - - if (Owner.HasComponent()) - { - return false; - } - - return true; - } - - public bool CanMove(IEntity entity) - { - if (entity == Owner) - { - return false; - } - - if (!entity.TryGetComponent(out IPhysBody? physics) || - physics.BodyType == BodyType.Static) - { - return false; - } - - if (entity.HasComponent()) - { - return false; - } - - if (entity.HasComponent()) - { - return false; - } - - if (entity.IsInContainer()) - { - return false; - } - - return true; - } - SuicideKind ISuicideAct.Suicide(IEntity victim, IChatManager chat) { var mind = victim.PlayerSession()?.ContentData()?.Mind; diff --git a/Content.Server/Recycling/RecyclerSystem.cs b/Content.Server/Recycling/RecyclerSystem.cs index 050f09a44c..f66adecb48 100644 --- a/Content.Server/Recycling/RecyclerSystem.cs +++ b/Content.Server/Recycling/RecyclerSystem.cs @@ -1,12 +1,17 @@ +using System.Collections.Generic; +using Content.Server.Power.Components; using Content.Server.Recycling.Components; using Content.Shared.Body.Components; using Content.Shared.Recycling; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Physics; using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Utility; namespace Content.Server.Recycling { - internal sealed class RecyclerSystem: EntitySystem + internal sealed class RecyclerSystem : EntitySystem { public override void Initialize() { @@ -21,11 +26,6 @@ namespace Content.Server.Recycling private void Recycle(RecyclerComponent component, IEntity entity) { - if (!component.Intersecting.Contains(entity)) - { - component.Intersecting.Add(entity); - } - // TODO: Prevent collision with recycled items // Can only recycle things that are recyclable... And also check the safety of the thing to recycle. @@ -45,7 +45,8 @@ namespace Content.Server.Recycling private bool CanGib(RecyclerComponent component, IEntity entity) { // We suppose this entity has a Recyclable component. - return entity.HasComponent() && !component.Safe && component.Powered; + return entity.HasComponent() && !component.Safe && + component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) && receiver.Powered; } public void Bloodstain(RecyclerComponent component) diff --git a/Resources/Prototypes/Entities/Structures/Machines/recycler.yml b/Resources/Prototypes/Entities/Structures/Machines/recycler.yml index 92cd7d374e..48b2478915 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/recycler.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/recycler.yml @@ -20,11 +20,17 @@ netsync: false sprite: Structures/Machines/recycling.rsi layers: - - state: grinder-o1 - map: ["enum.RecyclerVisualLayers.Bloody"] + - state: grinder-o0 + map: ["enum.RecyclerVisualLayers.Main"] - type: Appearance visuals: - type: RecyclerVisualizer - state_clean: grinder-o1 - state_bloody: grinder-o1bld + state_on: grinder-o1 + state_off: grinder-o0 - type: Recycler + - type: Conveyor + - type: SignalReceiver + inputs: + - name: state + type: Content.Shared.MachineLinking.TwoWayLeverSignal + maxConnections: 1 diff --git a/Resources/Prototypes/Entities/Structures/conveyor.yml b/Resources/Prototypes/Entities/Structures/conveyor.yml index a93d889ddf..3629ebdc63 100644 --- a/Resources/Prototypes/Entities/Structures/conveyor.yml +++ b/Resources/Prototypes/Entities/Structures/conveyor.yml @@ -13,12 +13,11 @@ !type:PhysShapeAabb bounds: "-0.49,-0.49,0.49,0.49" hard: false - layer: [Passable] - mask: - - Impassable - - MobImpassable - - VaultImpassable - - SmallImpassable + layer: + - Opaque + - Impassable + - MobImpassable + - VaultImpassable - type: SnapGrid - type: Sprite netsync: false