From 847461ec9356945841d7505160dd632b1dc7bd54 Mon Sep 17 00:00:00 2001 From: Acensti Date: Sun, 7 Jul 2024 14:10:24 +0200 Subject: [PATCH] throw what you see and see what sticks --- .../Buckle/Components/BuckleComponent.cs | 78 +++--- .../Buckle/Components/StrapComponent.cs | 18 ++ Content.Shared/Buckle/SharedBuckleSystem.cs | 248 ++++++++++++++---- 3 files changed, 248 insertions(+), 96 deletions(-) diff --git a/Content.Shared/Buckle/Components/BuckleComponent.cs b/Content.Shared/Buckle/Components/BuckleComponent.cs index ff304d46f24..1e05d348889 100644 --- a/Content.Shared/Buckle/Components/BuckleComponent.cs +++ b/Content.Shared/Buckle/Components/BuckleComponent.cs @@ -1,87 +1,81 @@ using Content.Shared.Interaction; using Robust.Shared.GameStates; using Robust.Shared.Serialization; - +using Content.Shared.Whitelist; namespace Content.Shared.Buckle.Components; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] + [Access(typeof(SharedBuckleSystem))] + public sealed partial class BuckleComponent : Component + { - /// - /// The range from which this entity can buckle to a . - /// Separated from normal interaction range to fix the "someone buckled to a strap - /// across a table two tiles away" problem. - /// - [DataField] - [ViewVariables(VVAccess.ReadWrite)] + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float Range = SharedInteractionSystem.InteractionRange / 1.4f; - /// - /// True if the entity is buckled, false otherwise. - /// + [ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public bool Buckled; - /// - /// The last entity this component was buckled to. - /// + [ViewVariables] + [AutoNetworkedField] + public EntityUid? LastEntityBuckledTo; - /// - /// Whether or not collisions should be possible with the entity we are strapped to. - /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField, AutoNetworkedField] + public bool DontCollide; - /// - /// Whether or not we should be allowed to pull the entity we are strapped to. - /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField] + public bool PullStrap; - /// - /// The delay before the buckle/unbuckle action is completed. - /// - [DataField] - [ViewVariables(VVAccess.ReadWrite)] + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan Delay = TimeSpan.FromSeconds(0.25f); - /// - /// The time when the buckle/unbuckle action was initiated. - /// + [ViewVariables] + public TimeSpan BuckleTime; - /// - /// The entity this component is currently buckled to. - /// + [ViewVariables] + [AutoNetworkedField] + public EntityUid? BuckledTo; - /// - /// The maximum size of entities that can be buckled to this component. - /// - [DataField] - [ViewVariables(VVAccess.ReadWrite)] + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int Size = 100; - /// - /// The original draw depth of the entity before it was buckled. - /// - [ViewVariables] public int? OriginalDrawDepth; - [DataField] + [ViewVariables] + + public int? OriginalDrawDepth; - [ViewVariables(VVAccess.ReadWrite)] + + [DataField, ViewVariables(VVAccess.ReadWrite)] public EntityWhitelist? AllowedBuckleTypes; + } [ByRefEvent] diff --git a/Content.Shared/Buckle/Components/StrapComponent.cs b/Content.Shared/Buckle/Components/StrapComponent.cs index e393cfd588b..dcb5cc0097c 100644 --- a/Content.Shared/Buckle/Components/StrapComponent.cs +++ b/Content.Shared/Buckle/Components/StrapComponent.cs @@ -1,5 +1,10 @@ using Robust.Shared.GameStates; using Robust.Shared.Serialization; +using Content.Shared.AlertLevel; +using Robust.Shared.Audio; +using System.Numerics; +using Content.Shared.Alert; +using Content.Shared.Whitelist; namespace Content.Shared.Buckle.Components; @@ -83,6 +88,19 @@ public sealed partial class StrapComponent : Component [ViewVariables(VVAccess.ReadWrite)] [AutoNetworkedField] public SoundSpecifier UnbuckleSound = new SoundPathSpecifier("/Audio/Effects/unbuckle.ogg"); + + /// + /// The allowed entities that can be buckled to this strap. + /// + [DataField] + [ViewVariables(VVAccess.ReadWrite)] + public EntityWhitelist? AllowedEntities; + + /// + /// The clamped buckle offset for this strap. + /// + [ViewVariables(VVAccess.ReadWrite)] + public Vector2 BuckleOffsetClamped => Vector2.Clamp(BuckleOffset, Vector2.One * -0.5f, Vector2.One * 0.5f); } public enum StrapPosition diff --git a/Content.Shared/Buckle/SharedBuckleSystem.cs b/Content.Shared/Buckle/SharedBuckleSystem.cs index 77b24115dff..6328415c085 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.cs @@ -1,10 +1,11 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.Verbs; -using Content.Shared.Physics; using Content.Shared.Throwing; using Content.Shared.Movement; using Content.Shared.Movement.Events; +using Robust.Shared.GameObjects; using Robust.Shared.GameStates; +using System.Linq; using Robust.Shared.Serialization; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; @@ -62,7 +63,8 @@ public override void Initialize() private void InitializeStrap() { - // Initialization logic for straps... + SubscribeLocalEvent(OnStrapStartup); + SubscribeLocalEvent(OnStrapShutdown); } private void InitializeBuckle() @@ -72,13 +74,107 @@ private void InitializeBuckle() SubscribeLocalEvent(OnBuckleMove); SubscribeLocalEvent(OnBuckleInteractHand); SubscribeLocalEvent>(AddUnbuckleVerb); - SubscribeLocalEvent(OnBucklePreventCollide); SubscribeLocalEvent(OnBuckleDownAttempt); SubscribeLocalEvent(OnBuckleStandAttempt); SubscribeLocalEvent(OnBuckleThrowPushbackAttempt); SubscribeLocalEvent(OnBuckleUpdateCanMove); } + private void OnStrapStartup(EntityUid uid, StrapComponent component, ComponentStartup args) + + { + + UpdateStrapVisuals(uid, component); + + + var buckleQuery = GetEntityQuery(); + + var xform = Transform(uid); + + var buckleEntities = GetEntitiesInRange(uid, component.MaxBuckleDistance); + + + foreach (var entity in buckleEntities) + + { + + if (buckleQuery.TryGetComponent(entity, out var buckle) && !buckle.Buckled) + + { + + TryBuckle(entity, entity, uid, buckle, component); + + } + + } + + } + public IEnumerable GetEntitiesInRange(EntityUid uid, float range, TransformComponent? xform = null) + { + if (!Resolve(uid, ref xform)) + yield break; + + var xformQuery = GetEntityQuery(); + var worldPos = _transform.GetWorldPosition(xform); + var rangeSquared = range * range; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var otherUid, out var otherXform)) + { + if (otherUid == uid) + continue; + + var otherWorldPos = _transform.GetWorldPosition(otherXform, xformQuery); + var displacement = otherWorldPos - worldPos; + + if (displacement.LengthSquared() <= rangeSquared) + { + yield return otherUid; + } + } + } + private void OnStrapShutdown(EntityUid uid, StrapComponent component, ComponentShutdown args) + + { + + // Unbuckle all entities from this strap + + foreach (var buckledEntity in component.BuckledEntities.ToList()) + + { + + TryUnbuckle(buckledEntity, buckledEntity, true); + + } + + + // Clear the buckled entities list + + component.BuckledEntities.Clear(); + + component.OccupiedSize = 0; + + + // Update the strap's visual state + + UpdateStrapVisuals(uid, component); + + } + + + private void UpdateStrapVisuals(EntityUid uid, StrapComponent? strap = null) + + { + + if (!Resolve(uid, ref strap)) + + return; + + + Appearance.SetData(uid, StrapVisuals.State, strap.BuckledEntities.Count > 0); + + } + private void OnBuckleStartup(EntityUid uid, BuckleComponent component, ComponentStartup args) { UpdateBuckleStatus(uid, component); @@ -94,7 +190,10 @@ private void OnBuckleMove(EntityUid uid, BuckleComponent component, ref MoveEven if (component.BuckledTo == null || component.BuckledTo == uid) return; - if (!_transform.GetWorldPosition(uid).InRange(_transform.GetWorldPosition(component.BuckledTo.Value), component.Range)) + var bucklePos = _transform.GetWorldPosition(uid); + var strapPos = _transform.GetWorldPosition(component.BuckledTo.Value); + + if ((bucklePos - strapPos).LengthSquared() > component.Range * component.Range) TryUnbuckle(uid, uid, true, component); } @@ -126,15 +225,6 @@ private void AddUnbuckleVerb(EntityUid uid, BuckleComponent component, GetVerbsE args.Verbs.Add(verb); } - private void OnBucklePreventCollide(EntityUid uid, BuckleComponent component, PreventCollideEvent args) - { - if (component.BuckledTo == null) - return; - - if (args.OtherEntity == component.BuckledTo) - args.Cancel(); - } - private void OnBuckleDownAttempt(EntityUid uid, BuckleComponent component, DownAttemptEvent args) { if (component.Buckled) @@ -164,8 +254,7 @@ private void UpdateBuckleStatus(EntityUid uid, BuckleComponent component) if (component.BuckledTo == null) return; - var strap = Comp(component.BuckledTo.Value); - if (strap == null) + if (!TryComp(component.BuckledTo.Value, out var strap)) return; if (strap.BuckledEntities.Contains(uid)) @@ -177,127 +266,171 @@ private void UpdateBuckleStatus(EntityUid uid, BuckleComponent component) } public bool TryBuckle(EntityUid buckleUid, EntityUid userUid, EntityUid strapUid, BuckleComponent? buckleComp = null, StrapComponent? strapComp = null) + { + if (!Resolve(buckleUid, ref buckleComp, false) || !Resolve(strapUid, ref strapComp, false)) + return false; + if (buckleComp.Buckled || !CanBuckle(buckleUid, userUid, strapUid, out strapComp, buckleComp)) + return false; + buckleComp.Buckled = true; + buckleComp.BuckledTo = strapUid; + buckleComp.BuckleTime = _gameTiming.CurTime; + strapComp.BuckledEntities.Add(buckleUid); + strapComp.OccupiedSize += buckleComp.Size; + ReAttach(buckleUid, strapUid, buckleComp, strapComp); + _audio.PlayPredicted(strapComp.BuckleSound, strapUid, userUid); + _alerts.ShowAlert(buckleUid, strapComp.BuckledAlertType); + var ev = new BuckleChangeEvent(strapUid, buckleUid, true); + RaiseLocalEvent(buckleUid, ref ev); + RaiseLocalEvent(strapUid, ref ev); + return true; - } + } public bool TryUnbuckle(EntityUid buckleUid, EntityUid userUid, bool force, BuckleComponent? buckleComp = null, StrapComponent? strapComp = null) + { - if (!Resolve(buckleUid, ref buckleComp, false) || !buckleComp.Buckled || !Resolve(buckleComp.BuckledTo, ref strapComp, false)) + + if (!Resolve(buckleUid, ref buckleComp, false) || !buckleComp.Buckled || buckleComp.BuckledTo == null || !Resolve(buckleComp.BuckledTo.Value, ref strapComp, false)) + return false; + if (!force && _gameTiming.CurTime < buckleComp.BuckleTime + buckleComp.Delay) + return false; + buckleComp.Buckled = false; - buckleComp.BuckledTo = null; + + buckleComp.BuckledTo = null!; + if (_mobState.IsIncapacitated(buckleUid)) + _standing.Down(buckleUid); + if (strapComp.BuckledEntities.Remove(buckleUid)) + { + strapComp.OccupiedSize -= buckleComp.Size; - Dirty(strapComp.Owner, strapComp); + + Dirty(buckleComp.BuckledTo.Value, strapComp); + } + _joints.RefreshRelay(buckleUid); - Appearance.SetData(strapComp.Owner, StrapVisuals.State, strapComp.BuckledEntities.Count != 0); - if (!TerminatingOrDeleted(strapComp.Owner)) - _audio.PlayPredicted(strapComp.UnbuckleSound, strapComp.Owner, userUid); + Appearance.SetData(buckleComp.BuckledTo.Value, StrapVisuals.State, strapComp.BuckledEntities.Count != 0); + + + if (!TerminatingOrDeleted(buckleComp.BuckledTo.Value)) + + _audio.PlayPredicted(strapComp.UnbuckleSound, buckleComp.BuckledTo.Value, userUid); + + + var ev = new BuckleChangeEvent(buckleComp.BuckledTo.Value, buckleUid, false); - var ev = new BuckleChangeEvent(strapComp.Owner, buckleUid, false); RaiseLocalEvent(buckleUid, ref ev); - RaiseLocalEvent(strapComp.Owner, ref ev); + + RaiseLocalEvent(buckleComp.BuckledTo.Value, ref ev); + return true; + } + private bool CanBuckle(EntityUid buckleUid, EntityUid userUid, EntityUid strapUid, [NotNullWhen(true)] out StrapComponent? strapComp, BuckleComponent? buckleComp = null) + { + strapComp = null; - // Guard clauses - if (userUid == strapUid || !Resolve(buckleUid, ref buckleComp, false) || !Resolve(strapUid, ref strapComp, false)) + + if (!Resolve(buckleUid, ref buckleComp, false) || !Resolve(strapUid, ref strapComp, false)) + return false; - if (!IsAllowedBuckleType(buckleComp, userUid)) + + if (userUid == strapUid) + return false; - if (!IsStrapEnabled(strapComp)) + + if (!ActionBlocker.CanInteract(userUid, strapUid)) + return false; - if (!IsStrapSizeValid(buckleComp, strapComp)) + + if (!IsEntityAllowed(buckleUid, strapComp)) + return false; - if (!IsInRange(buckleUid, strapUid, buckleComp)) + + if (!_interaction.InRangeUnobstructed(buckleUid, strapUid, buckleComp.Range)) + return false; - if (!IsEntityAllowed(buckleUid, strapComp)) + + if (strapComp.OccupiedSize + buckleComp.Size > strapComp.Size) + return false; - return true; - } - private bool IsAllowedBuckleType(BuckleComponent? buckleComp, EntityUid userUid) - { - if (buckleComp?.AllowedBuckleTypes != null && !buckleComp.AllowedBuckleTypes.IsValid(userUid, EntityManager)) - { - if (_netManager.IsServer) - _popup.PopupEntity(Loc.GetString("buckle-component-cannot-fit-message"), userUid, buckleUid, PopupType.Medium); + if (!strapComp.Enabled) + return false; - } - return true; - } - private bool IsStrapEnabled(StrapComponent? strapComp) - => strapComp?.Enabled == true; + return true; - private bool IsStrapSizeValid(BuckleComponent? buckleComp, StrapComponent? strapComp) - => buckleComp != null && strapComp != null && strapComp.OccupiedSize + buckleComp.Size <= strapComp.Size; + } - private bool IsInRange(EntityUid buckleUid, EntityUid strapUid, BuckleComponent? buckleComp) - => buckleComp != null && _interaction.InRangeUnobstructed(buckleUid, strapUid, buckleComp.Range); + private bool IsEntityAllowed(EntityUid buckleUid, StrapComponent strapComp) + { + return strapComp.AllowedEntities == null || strapComp.AllowedEntities.IsValid(buckleUid, EntityManager); + } - private bool IsEntityAllowed(EntityUid buckleUid, StrapComponent? strapComp) - => strapComp?.AllowedEntities == null || strapComp.AllowedEntities.IsValid(buckleUid); + private bool IsInRange(EntityUid buckleUid, EntityUid strapUid, float range) + { + return _interaction.InRangeUnobstructed(buckleUid, strapUid, range); + } private void ReAttach(EntityUid buckleUid, EntityUid strapUid, BuckleComponent? buckleComp = null, StrapComponent? strapComp = null) { if (!Resolve(strapUid, ref strapComp, false) || !Resolve(buckleUid, ref buckleComp, false)) return; - _transform.SetCoordinates(buckleUid, new EntityCoordinates(strapUid, strapComp.BuckleOffsetClamped)); + _transform.SetCoordinates(buckleUid, new EntityCoordinates(strapUid, strapComp.BuckleOffset)); var buckleTransform = Transform(buckleUid); - // Buckle subscribes to move for < reasons > so this might fail. - // TODO: Make buckle not do that. - if (buckleTransform.ParentUid != strapUid) return; @@ -316,4 +449,11 @@ private void ReAttach(EntityUid buckleUid, EntityUid strapUid, BuckleComponent? break; } } + public enum StrapVisuals + + { + + State + + } }