#nullable enable using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects.Components.Conveyor; using Content.Shared.GameObjects.Components.Interactable; using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Physics; using Content.Shared.Utility; using Robust.Server.GameObjects; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components; using Robust.Shared.GameObjects.Components.Map; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Random; using Robust.Shared.IoC; using Robust.Shared.Maths; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Conveyor { [RegisterComponent] public class ConveyorComponent : Component, IInteractUsing { [Dependency] private readonly IEntityManager _entityManager = default!; public override string Name => "Conveyor"; /// /// The angle to move entities by in relation to the owner's rotation. /// [ViewVariables] private Angle _angle; /// /// The amount of units to move the entity by per second. /// [ViewVariables(VVAccess.ReadWrite)] private float _speed; private ConveyorState _state; /// /// The current state of this conveyor /// [ViewVariables(VVAccess.ReadWrite)] private ConveyorState State { get => _state; set { _state = value; if (!Owner.TryGetComponent(out AppearanceComponent? appearance)) { return; } appearance.SetData(ConveyorVisuals.State, value); } } private ConveyorGroup? _group = new ConveyorGroup(); /// /// 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 /// private Angle GetAngle() { var adjustment = _state == ConveyorState.Reversed ? MathHelper.Pi : 0; var radians = MathHelper.DegreesToRadians(_angle); return new Angle(Owner.Transform.LocalRotation.Theta + radians + adjustment); } private bool CanRun() { if (State == ConveyorState.Off) { return false; } if (Owner.TryGetComponent(out PowerReceiverComponent? receiver) && !receiver.Powered) { return false; } if (Owner.HasComponent()) { return false; } return true; } private bool CanMove(IEntity entity) { if (entity == Owner) { return false; } if (!entity.TryGetComponent(out IPhysicsComponent? physics) || physics.Anchored) { return false; } if (entity.HasComponent()) { return false; } if (entity.HasComponent()) { return false; } if (ContainerHelpers.IsInContainer(entity)) { return false; } return true; } public void Update(float frameTime) { if (!CanRun()) { return; } var intersecting = _entityManager.GetEntitiesIntersecting(Owner, true); var direction = GetAngle().ToVec(); foreach (var entity in intersecting) { if (!CanMove(entity)) { continue; } if (entity.TryGetComponent(out IPhysicsComponent? physics)) { var controller = physics.EnsureController(); controller.Move(direction, _speed); } } } private async Task ToolUsed(IEntity user, ToolComponent tool) { if (!Owner.HasComponent() && await tool.UseTool(user, Owner, 0.5f, ToolQuality.Prying)) { State = ConveyorState.Loose; Owner.AddComponent(); _group?.RemoveConveyor(this); Owner.RandomOffset(0.2f); return true; } return false; } public void Sync(ConveyorGroup group) { _group = group; if (State == ConveyorState.Loose) { return; } State = group.State == ConveyorState.Loose ? ConveyorState.Off : group.State; } /// /// Disconnects this conveyor from any switch. /// private void Disconnect() { _group?.RemoveConveyor(this); _group = null; State = ConveyorState.Off; } public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); serializer.DataReadWriteFunction( "switches", new List(), ids => { if (ids == null) { return; } foreach (var id in ids) { if (!Owner.EntityManager.TryGetEntity(id, out var @switch)) { continue; } if (!@switch.TryGetComponent(out ConveyorSwitchComponent? component)) { continue; } component.Connect(this); } }, () => _group?.Switches.Select(@switch => @switch.Owner.Uid).ToList()); serializer.DataField(ref _angle, "angle", 0); serializer.DataField(ref _speed, "speed", 2); } public override void OnRemove() { base.OnRemove(); Disconnect(); } async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent? conveyorSwitch)) { conveyorSwitch.Connect(this, eventArgs.User); return true; } if (eventArgs.Using.TryGetComponent(out ToolComponent? tool)) { return await ToolUsed(eventArgs.User, tool); } return false; } } }