using System.Linq; using Content.Server.Interaction; using Content.Server.Mech.Equipment.Components; using Content.Server.Mech.Systems; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared.Mech; using Content.Shared.Mech.Components; using Content.Shared.Mech.Equipment.Components; using Content.Shared.Mobs.Components; using Content.Shared.Wall; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; namespace Content.Server.Mech.Equipment.EntitySystems; /// /// Handles and all related UI logic /// public sealed class MechGrabberSystem : EntitySystem { [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly MechSystem _mech = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly InteractionSystem _interaction = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly TransformSystem _transform = default!; /// public override void Initialize() { SubscribeLocalEvent(OnGrabberMessage); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnUiStateReady); SubscribeLocalEvent(OnEquipmentRemoved); SubscribeLocalEvent(OnAttemptRemove); SubscribeLocalEvent(OnInteract); SubscribeLocalEvent(OnMechGrab); } private void OnGrabberMessage(EntityUid uid, MechGrabberComponent component, MechEquipmentUiMessageRelayEvent args) { if (args.Message is not MechGrabberEjectMessage msg) return; if (!TryComp(uid, out var equipmentComponent) || equipmentComponent.EquipmentOwner == null) return; var mech = equipmentComponent.EquipmentOwner.Value; var targetCoords = new EntityCoordinates(mech, component.DepositOffset); if (!_interaction.InRangeUnobstructed(mech, targetCoords)) return; var item = GetEntity(msg.Item); if (!component.ItemContainer.Contains(item)) return; RemoveItem(uid, mech, item, component); } /// /// Removes an item from the grabber's container /// /// The mech grabber /// The mech it belongs to /// The item being removed /// public void RemoveItem(EntityUid uid, EntityUid mech, EntityUid toRemove, MechGrabberComponent? component = null) { if (!Resolve(uid, ref component)) return; _container.Remove(toRemove, component.ItemContainer); var mechxform = Transform(mech); var xform = Transform(toRemove); _transform.AttachToGridOrMap(toRemove, xform); var (mechPos, mechRot) = _transform.GetWorldPositionRotation(mechxform); var offset = mechPos + mechRot.RotateVec(component.DepositOffset); _transform.SetWorldPositionRotation(toRemove, offset, Angle.Zero); _mech.UpdateUserInterface(mech); } private void OnEquipmentRemoved(EntityUid uid, MechGrabberComponent component, ref MechEquipmentRemovedEvent args) { if (!TryComp(uid, out var equipmentComponent) || equipmentComponent.EquipmentOwner == null) return; var mech = equipmentComponent.EquipmentOwner.Value; var allItems = new List(component.ItemContainer.ContainedEntities); foreach (var item in allItems) { RemoveItem(uid, mech, item, component); } } private void OnAttemptRemove(EntityUid uid, MechGrabberComponent component, ref AttemptRemoveMechEquipmentEvent args) { args.Cancelled = component.ItemContainer.ContainedEntities.Any(); } private void OnStartup(EntityUid uid, MechGrabberComponent component, ComponentStartup args) { component.ItemContainer = _container.EnsureContainer(uid, "item-container"); } private void OnUiStateReady(EntityUid uid, MechGrabberComponent component, MechEquipmentUiStateReadyEvent args) { var state = new MechGrabberUiState { Contents = GetNetEntityList(component.ItemContainer.ContainedEntities.ToList()), MaxContents = component.MaxContents }; args.States.Add(GetNetEntity(uid), state); } private void OnInteract(EntityUid uid, MechGrabberComponent component, UserActivateInWorldEvent args) { if (args.Handled) return; var target = args.Target; if (args.Target == args.User || component.DoAfter != null) return; if (TryComp(target, out var physics) && physics.BodyType == BodyType.Static || HasComp(target) || HasComp(target)) { return; } if (Transform(target).Anchored) return; if (component.ItemContainer.ContainedEntities.Count >= component.MaxContents) return; if (!TryComp(args.User, out var mech) || mech.PilotSlot.ContainedEntity == target) return; if (mech.Energy + component.GrabEnergyDelta < 0) return; if (!_interaction.InRangeUnobstructed(args.User, target)) return; args.Handled = true; component.AudioStream = _audio.PlayPvs(component.GrabSound, uid)?.Entity; var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.GrabDelay, new GrabberDoAfterEvent(), uid, target: target, used: uid) { BreakOnMove = true }; _doAfter.TryStartDoAfter(doAfterArgs, out component.DoAfter); } private void OnMechGrab(EntityUid uid, MechGrabberComponent component, DoAfterEvent args) { component.DoAfter = null; if (args.Cancelled) { component.AudioStream = _audio.Stop(component.AudioStream); return; } if (args.Handled || args.Args.Target == null) return; if (!TryComp(uid, out var equipmentComponent) || equipmentComponent.EquipmentOwner == null) return; if (!_mech.TryChangeEnergy(equipmentComponent.EquipmentOwner.Value, component.GrabEnergyDelta)) return; _container.Insert(args.Args.Target.Value, component.ItemContainer); _mech.UpdateUserInterface(equipmentComponent.EquipmentOwner.Value); args.Handled = true; } }