using System.Linq; using Content.Shared.Alert; using Content.Shared.Buckle.Components; using Content.Shared.DragDrop; using Robust.Shared.Audio; using Robust.Shared.Serialization; namespace Content.Server.Buckle.Components { [RegisterComponent] [ComponentReference(typeof(SharedStrapComponent))] public sealed class StrapComponent : SharedStrapComponent { [Dependency] private readonly IEntityManager _entityManager = default!; /// /// The angle in degrees to rotate the player by when they get strapped /// [ViewVariables] [DataField("rotation")] private int _rotation; /// /// The size of the strap which is compared against when buckling entities /// [ViewVariables] [DataField("size")] private int _size = 100; private int _occupiedSize; private bool _enabled = true; /// /// If disabled, nothing can be buckled on this object, and it will unbuckle anything that's already buckled /// public bool Enabled { get => _enabled; set { _enabled = value; if (_enabled == value) return; RemoveAll(); } } /// /// You can specify the offset the entity will have after unbuckling. /// [DataField("unbuckleOffset", required: false)] public Vector2 UnbuckleOffset = Vector2.Zero; /// /// The sound to be played when a mob is buckled /// [ViewVariables] [DataField("buckleSound")] public SoundSpecifier BuckleSound { get; } = new SoundPathSpecifier("/Audio/Effects/buckle.ogg"); /// /// The sound to be played when a mob is unbuckled /// [ViewVariables] [DataField("unbuckleSound")] public SoundSpecifier UnbuckleSound { get; } = new SoundPathSpecifier("/Audio/Effects/unbuckle.ogg"); /// /// ID of the alert to show when buckled /// [ViewVariables] [DataField("buckledAlertType")] public AlertType BuckledAlertType { get; } = AlertType.Buckled; /// /// The sum of the sizes of all the buckled entities in this strap /// [ViewVariables] public int OccupiedSize => _occupiedSize; /// /// Checks if this strap has enough space for a new occupant. /// /// The new occupant /// true if there is enough space, false otherwise public bool HasSpace(BuckleComponent buckle) { return OccupiedSize + buckle.Size <= _size; } /// /// DO NOT CALL THIS DIRECTLY. /// Adds a buckled entity. Called from /// /// The component to add /// /// Whether or not to check if the strap has enough space /// /// True if added, false otherwise public bool TryAdd(BuckleComponent buckle, bool force = false) { if (!Enabled) return false; if (!force && !HasSpace(buckle)) { return false; } if (!BuckledEntities.Add(buckle.Owner)) { return false; } _occupiedSize += buckle.Size; if(_entityManager.TryGetComponent(buckle.Owner, out var appearanceComponent)) appearanceComponent.SetData(StrapVisuals.RotationAngle, _rotation); // Update the visuals of the strap object if (IoCManager.Resolve().TryGetComponent(Owner, out var appearance)) { appearance.SetData("StrapState", true); } Dirty(); return true; } /// /// Removes a buckled entity. /// Called from /// /// The component to remove public void Remove(BuckleComponent buckle) { if (BuckledEntities.Remove(buckle.Owner)) { if (IoCManager.Resolve().TryGetComponent(Owner, out var appearance)) { appearance.SetData("StrapState", false); } _occupiedSize -= buckle.Size; Dirty(); } } protected override void OnRemove() { base.OnRemove(); RemoveAll(); } public void RemoveAll() { var entManager = IoCManager.Resolve(); foreach (var entity in BuckledEntities.ToArray()) { if (entManager.TryGetComponent(entity, out var buckle)) { buckle.TryUnbuckle(entity, true); } } BuckledEntities.Clear(); _occupiedSize = 0; Dirty(); } public override bool DragDropOn(DragDropEvent eventArgs) { var entManager = IoCManager.Resolve(); if (!entManager.TryGetComponent(eventArgs.Dragged, out BuckleComponent? buckleComponent)) return false; return buckleComponent.TryBuckle(eventArgs.User, Owner); } } }