using Content.Server.DeviceLinking.Systems; using Content.Server.Materials; using Content.Shared.Conveyor; using Content.Shared.DeviceLinking.Events; using Content.Shared.Destructible; using Content.Shared.Maps; using Content.Shared.Physics; using Content.Shared.Physics.Controllers; using Content.Shared.Power; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Systems; namespace Content.Server.Physics.Controllers; public sealed class ConveyorController : SharedConveyorController { [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!; [Dependency] private readonly MaterialReclaimerSystem _materialReclaimer = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly TurfSystem _turf = default!; public override void Initialize() { UpdatesAfter.Add(typeof(MoverController)); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnConveyorShutdown); SubscribeLocalEvent(OnBreakage); SubscribeLocalEvent(OnSignalReceived); SubscribeLocalEvent(OnPowerChanged); base.Initialize(); } private void OnInit(EntityUid uid, ConveyorComponent component, ComponentInit args) { _signalSystem.EnsureSinkPorts(uid, component.ReversePort, component.ForwardPort, component.OffPort); if (PhysicsQuery.TryComp(uid, out var physics)) { var shape = new PolygonShape(); shape.SetAsBox(0.55f, 0.55f); _fixtures.TryCreateFixture(uid, shape, ConveyorFixture, collisionLayer: (int) (CollisionGroup.LowImpassable | CollisionGroup.MidImpassable | CollisionGroup.Impassable), hard: false, body: physics); } } private void OnConveyorShutdown(EntityUid uid, ConveyorComponent component, ComponentShutdown args) { if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating) return; if (!PhysicsQuery.TryComp(uid, out var physics)) return; _fixtures.DestroyFixture(uid, ConveyorFixture, body: physics); } private void OnBreakage(Entity ent, ref BreakageEventArgs args) { SetState(ent, ConveyorState.Off, ent); } private void OnPowerChanged(EntityUid uid, ConveyorComponent component, ref PowerChangedEvent args) { component.Powered = args.Powered; UpdateAppearance(uid, component); Dirty(uid, component); } private void UpdateAppearance(EntityUid uid, ConveyorComponent component) { _appearance.SetData(uid, ConveyorVisuals.State, component.Powered ? component.State : ConveyorState.Off); } private void OnSignalReceived(EntityUid uid, ConveyorComponent component, ref SignalReceivedEvent args) { if (args.Port == component.OffPort) SetState(uid, ConveyorState.Off, component); else if (args.Port == component.ForwardPort) { SetState(uid, ConveyorState.Forward, component); } else if (args.Port == component.ReversePort) { SetState(uid, ConveyorState.Reverse, component); } } private void SetState(EntityUid uid, ConveyorState state, ConveyorComponent? component = null) { if (!Resolve(uid, ref component)) return; if (!_materialReclaimer.SetReclaimerEnabled(uid, state != ConveyorState.Off)) return; component.State = state; if (state != ConveyorState.Off) { WakeConveyed(uid); } UpdateAppearance(uid, component); Dirty(uid, component); } /// /// Awakens sleeping entities on the conveyor belt's tile when it's turned on. /// Need this as we might activate under CollisionWake entities and need to forcefully check them. /// protected override void AwakenConveyor(Entity ent) { if (!XformQuery.Resolve(ent.Owner, ref ent.Comp)) return; var xform = ent.Comp; var beltTileRef = _turf.GetTileRef(xform.Coordinates); if (beltTileRef != null) { Intersecting.Clear(); Lookup.GetLocalEntitiesIntersecting(beltTileRef.Value.GridUid, beltTileRef.Value.GridIndices, Intersecting, 0f, flags: LookupFlags.Dynamic | LookupFlags.Sundries | LookupFlags.Approximate); foreach (var entity in Intersecting) { if (!PhysicsQuery.TryGetComponent(entity, out var physics)) continue; if (physics.BodyType != BodyType.Static) PhysicsSystem.WakeBody(entity, body: physics); } } } }