using System.Collections.Generic; using Content.Server.GameObjects.Components.Buckle; using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Strap; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces.GameObjects.Components; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Localization; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Strap { [RegisterComponent] public class StrapComponent : SharedStrapComponent, IInteractHand { private HashSet _buckledEntities; private StrapPosition _position; private string _buckleSound; private string _unbuckleSound; private string _buckledIcon; /// /// The angle in degrees to rotate the player by when they get strapped /// [ViewVariables] private int _rotation; /// /// The size of the strap which is compared against when buckling entities /// [ViewVariables] private int _size; private int _occupiedSize; /// /// The entity that is currently buckled here, synced from /// public IReadOnlyCollection BuckledEntities => _buckledEntities; /// /// The change in position to the strapped mob /// public StrapPosition Position => _position; /// /// The sound to be played when a mob is buckled /// [ViewVariables] public string BuckleSound => _buckleSound; /// /// The sound to be played when a mob is unbuckled /// [ViewVariables] public string UnbuckleSound => _unbuckleSound; /// /// The icon to be displayed as a status when buckled /// [ViewVariables] public string BuckledIcon => _buckledIcon; /// /// 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; } /// /// 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 (!force && !HasSpace(buckle)) { return false; } if (!_buckledEntities.Add(buckle.Owner)) { return false; } _occupiedSize += buckle.Size; if (buckle.Owner.TryGetComponent(out AppearanceComponent appearance)) { appearance.SetData(StrapVisuals.RotationAngle, _rotation); } return true; } /// /// Removes a buckled entity. /// Called from /// /// The component to remove public void Remove(BuckleComponent buckle) { if (_buckledEntities.Remove(buckle.Owner)) { _occupiedSize -= buckle.Size; } } public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); serializer.DataField(ref _position, "position", StrapPosition.None); serializer.DataField(ref _buckleSound, "buckleSound", "/Audio/Effects/buckle.ogg"); serializer.DataField(ref _unbuckleSound, "unbuckleSound", "/Audio/Effects/unbuckle.ogg"); serializer.DataField(ref _buckledIcon, "buckledIcon", "/Textures/Interface/StatusEffects/Buckle/buckled.png"); serializer.DataField(ref _rotation, "rotation", 0); var defaultSize = 100; serializer.DataField(ref _size, "size", defaultSize); _buckledEntities = new HashSet(_size / defaultSize); _occupiedSize = 0; } public override void OnRemove() { base.OnRemove(); foreach (var entity in _buckledEntities) { if (entity.TryGetComponent(out BuckleComponent buckle)) { buckle.TryUnbuckle(entity, true); } } _buckledEntities.Clear(); _occupiedSize = 0; } bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs) { if (!eventArgs.User.TryGetComponent(out BuckleComponent buckle)) { return false; } return buckle.ToggleBuckle(eventArgs.User, Owner); } [Verb] private sealed class StrapVerb : Verb { protected override void GetData(IEntity user, StrapComponent component, VerbData data) { data.Visibility = VerbVisibility.Invisible; if (!ActionBlockerSystem.CanInteract(component.Owner) || !user.TryGetComponent(out BuckleComponent buckle) || buckle.BuckledTo != null && buckle.BuckledTo != component || user == component.Owner) { return; } var parent = component.Owner.Transform.Parent; while (parent != null) { if (parent == user.Transform) { return; } parent = parent.Parent; } var userPosition = user.Transform.MapPosition; var strapPosition = component.Owner.Transform.MapPosition; var range = SharedInteractionSystem.InteractionRange / 2; var inRange = EntitySystem.Get() .InRangeUnobstructed(userPosition, strapPosition, range, predicate: entity => entity == user || entity == component.Owner); if (!inRange) { return; } data.Visibility = VerbVisibility.Visible; data.Text = buckle.BuckledTo == null ? Loc.GetString("Buckle") : Loc.GetString("Unbuckle"); } protected override void Activate(IEntity user, StrapComponent component) { if (!user.TryGetComponent(out BuckleComponent buckle)) { return; } buckle.ToggleBuckle(user, component.Owner); } } } }