using System.Numerics; using Content.Shared.Alert; using Content.Shared.Vehicle; using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Serialization; namespace Content.Shared.Buckle.Components; [RegisterComponent, NetworkedComponent] [Access(typeof(SharedBuckleSystem), typeof(SharedVehicleSystem))] public sealed partial class StrapComponent : Component { /// /// The entities that are currently buckled /// [ViewVariables] // TODO serialization public readonly HashSet BuckledEntities = new(); /// /// Entities that this strap accepts and can buckle /// If null it accepts any entity /// [DataField("allowedEntities")] [ViewVariables] public EntityWhitelist? AllowedEntities; /// /// The change in position to the strapped mob /// [DataField("position")] [ViewVariables(VVAccess.ReadWrite)] public StrapPosition Position = StrapPosition.None; /// /// The distance above which a buckled entity will be automatically unbuckled. /// Don't change it unless you really have to /// /// /// Dont set this below 0.2 because that causes audio issues with /// My guess after testing is that the client sets BuckledTo to the strap in *some* ticks for some reason /// whereas the server doesnt, thus the client tries to unbuckle like 15 times because it passes the strap null check /// This is why this needs to be above 0.1 to make the InRange check fail in both client and server. /// [DataField("maxBuckleDistance", required: false)] [ViewVariables(VVAccess.ReadWrite)] public float MaxBuckleDistance = 0.2f; /// /// Gets and clamps the buckle offset to MaxBuckleDistance /// [ViewVariables] public Vector2 BuckleOffset => Vector2.Clamp( BuckleOffsetUnclamped, Vector2.One * -MaxBuckleDistance, Vector2.One * MaxBuckleDistance); /// /// The buckled entity will be offset by this amount from the center of the strap object. /// If this offset it too big, it will be clamped to /// [DataField("buckleOffset", required: false)] [ViewVariables(VVAccess.ReadWrite)] public Vector2 BuckleOffsetUnclamped = Vector2.Zero; /// /// The angle in degrees to rotate the player by when they get strapped /// [DataField("rotation")] [ViewVariables(VVAccess.ReadWrite)] public int Rotation; /// /// The size of the strap which is compared against when buckling entities /// [DataField("size")] [ViewVariables(VVAccess.ReadWrite)] public int Size = 100; /// /// If disabled, nothing can be buckled on this object, and it will unbuckle anything that's already buckled /// [ViewVariables] public bool Enabled = true; /// /// You can specify the offset the entity will have after unbuckling. /// [DataField("unbuckleOffset", required: false)] [ViewVariables(VVAccess.ReadWrite)] public Vector2 UnbuckleOffset = Vector2.Zero; /// /// The sound to be played when a mob is buckled /// [DataField("buckleSound")] [ViewVariables(VVAccess.ReadWrite)] public SoundSpecifier BuckleSound = new SoundPathSpecifier("/Audio/Effects/buckle.ogg"); /// /// The sound to be played when a mob is unbuckled /// [DataField("unbuckleSound")] [ViewVariables(VVAccess.ReadWrite)] public SoundSpecifier UnbuckleSound = new SoundPathSpecifier("/Audio/Effects/unbuckle.ogg"); /// /// ID of the alert to show when buckled /// [DataField("buckledAlertType")] [ViewVariables(VVAccess.ReadWrite)] public AlertType BuckledAlertType = AlertType.Buckled; /// /// The sum of the sizes of all the buckled entities in this strap /// [ViewVariables] public int OccupiedSize; } [Serializable, NetSerializable] public sealed class StrapComponentState : ComponentState { public readonly StrapPosition Position; public readonly float MaxBuckleDistance; public readonly Vector2 BuckleOffsetClamped; public readonly HashSet BuckledEntities; public readonly int OccupiedSize; public StrapComponentState(StrapPosition position, Vector2 offset, HashSet buckled, float maxBuckleDistance, int occupiedSize) { Position = position; BuckleOffsetClamped = offset; BuckledEntities = buckled; MaxBuckleDistance = maxBuckleDistance; OccupiedSize = occupiedSize; } } public enum StrapPosition { /// /// (Default) Makes no change to the buckled mob /// None = 0, /// /// Makes the mob stand up /// Stand, /// /// Makes the mob lie down /// Down } [Serializable, NetSerializable] public enum StrapVisuals : byte { RotationAngle, State }