From 30fc5f249cbbc4a0ac222464963f5c91afd86cff Mon Sep 17 00:00:00 2001 From: fox Date: Mon, 23 Sep 2024 15:50:29 +0300 Subject: [PATCH] Rework --- .../Leash/Components/LeashComponent.cs | 8 +- .../Floofstation/Leash/LeashSystem.cs | 111 +++++++++++++++--- 2 files changed, 103 insertions(+), 16 deletions(-) diff --git a/Content.Shared/Floofstation/Leash/Components/LeashComponent.cs b/Content.Shared/Floofstation/Leash/Components/LeashComponent.cs index 7d17ecb8e8..181d61b071 100644 --- a/Content.Shared/Floofstation/Leash/Components/LeashComponent.cs +++ b/Content.Shared/Floofstation/Leash/Components/LeashComponent.cs @@ -55,8 +55,12 @@ public sealed partial class LeashComponent : Component [DataDefinition, Serializable, NetSerializable] public sealed partial class LeashData { + /// + /// Id of the joint created by this leash. May be null if this leash does not currently create a joint + /// (e.g. because it's attached to the same entity who holds it) + /// [DataField] - public string JointId = string.Empty; + public string? JointId = null; [DataField] public NetEntity Pulled = NetEntity.Invalid; @@ -67,7 +71,7 @@ public sealed partial class LeashData [DataField] public NetEntity? LeashVisuals = null; - public LeashData(string jointId, NetEntity pulled) + public LeashData(string? jointId, NetEntity pulled) { JointId = jointId; Pulled = pulled; diff --git a/Content.Shared/Floofstation/Leash/LeashSystem.cs b/Content.Shared/Floofstation/Leash/LeashSystem.cs index ca52d8928c..9b4bd293f9 100644 --- a/Content.Shared/Floofstation/Leash/LeashSystem.cs +++ b/Content.Shared/Floofstation/Leash/LeashSystem.cs @@ -45,6 +45,9 @@ public override void Initialize() SubscribeLocalEvent(OnJointRemoved, after: [typeof(SharedJointSystem)]); SubscribeLocalEvent>(OnGetLeashedVerbs); + SubscribeLocalEvent(OnLeashInserted); + SubscribeLocalEvent(OnLeashRemoved); + SubscribeLocalEvent(OnAttachDoAfter); SubscribeLocalEvent(OnDetachDoAfter); @@ -74,6 +77,7 @@ public override void Update(float frameTime) // Client side only: set max distance to infinity to prevent the client from ever predicting leashes. if (_net.IsClient + && data.JointId is not null && TryComp(target, out var jointComp) && jointComp.GetJoints.TryGetValue(data.JointId, out var joint) && joint is DistanceJoint distanceJoint @@ -167,17 +171,37 @@ private void OnJointRemoved(Entity ent, ref JointRemovedEvent || ent.Comp.Puller is not { } puller || !TryComp(ent.Comp.Anchor, out var anchor) || !TryComp(puller, out var leash) - || leash.Leashed.All(it => it.JointId != id) - || !Transform(ent).Coordinates.TryDistance(EntityManager, Transform(puller).Coordinates, out var dst) - || dst > leash.MaxDistance - ) + || leash.Leashed.All(it => it.JointId != id)) return; - // If the entity still has a leashed comp, and is on the same map, and is within the max distance of the leash - // Then the leash was likely broken due to some weird unforeseen fucking robust toolbox magic. We can try to recreate it. - // This is hella unsafe to do. It will crash in debug builds under certain conditions. Luckily, release builds are safe. RemoveLeash(ent!, (puller, leash), false); - DoLeash((ent.Comp.Anchor.Value, anchor), (puller, leash), ent); + + // If the entity still has a leashed comp, and is on the same map, and is within the max distance of the leash + // Then the leash was likely broken due to some weird unforeseen fucking robust toolbox magic. + // We can try to recreate it, but on the next tick. + Timer.Spawn(0, () => + { + if (TerminatingOrDeleted(ent.Comp.Anchor.Value) + || TerminatingOrDeleted(puller) + || !Transform(ent).Coordinates.TryDistance(EntityManager, Transform(puller).Coordinates, out var dst) + || dst > leash.MaxDistance + ) + return; + + DoLeash((ent.Comp.Anchor.Value, anchor), (puller, leash), ent); + }); + } + + private void OnLeashInserted(Entity ent, ref EntGotInsertedIntoContainerMessage args) + { + if (!_net.IsClient) + RefreshJoints(ent); + } + + private void OnLeashRemoved(Entity ent, ref EntGotRemovedFromContainerMessage args) + { + if (!_net.IsClient) + RefreshJoints(ent); } private void OnAttachDoAfter(Entity ent, ref LeashAttachDoAfterEvent args) @@ -257,9 +281,27 @@ private bool TryGetLeashTarget(Entity ent, out EntityUid return true; } + /// + /// Returns true if a leash joint can be created between the two specified entities. + /// This will return false if one of the entities is a parent of another. + /// + public bool CanCreateJoint(EntityUid a, EntityUid b) + { + BaseContainer? aOuter = null, bOuter = null; + + // If neither of the entities are in contianers, it's safe to create a joint + if (!_container.TryGetOuterContainer(a, Transform(a), out aOuter) + && !_container.TryGetOuterContainer(b, Transform(b), out bOuter)) + return true; + + // Otherwise, we need to make sure that neither of the entities contain the other, and that they are not in the same container. + return a != bOuter?.Owner && b != aOuter?.Owner && aOuter?.Owner != bOuter?.Owner; + } + private DistanceJoint CreateLeashJoint(string jointId, Entity leash, EntityUid leashTarget) { var joint = _joints.CreateDistanceJoint(leash, leashTarget, id: jointId); + joint.CollideConnected = false; joint.Length = leash.Comp.Length; joint.MinLength = 0f; @@ -281,10 +323,10 @@ public bool CanLeash(Entity anchor, Entity && TryGetLeashTarget(anchor!, out var leashTarget) && CompOrNull(leashTarget)?.JointId == null && Transform(anchor).Coordinates.TryDistance(EntityManager, Transform(leash).Coordinates, out var dst) - && dst <= leash.Comp.Length - && !_xform.IsParentOf(Transform(leashTarget), leash); // google recursion - this makes the game explode for some reason + && dst <= leash.Comp.Length; } + public bool TryLeash(Entity anchor, Entity leash, EntityUid user, bool popup = true) { if (!CanLeash(anchor, leash) || !TryGetLeashTarget(anchor!, out var leashTarget)) @@ -362,13 +404,21 @@ public void DoLeash(Entity anchor, Entity var leashedComp = EnsureComp(leashTarget); var netLeashTarget = GetNetEntity(leashTarget); - leashedComp.JointId = $"leash-joint-{netLeashTarget}"; + var data = new LeashComponent.LeashData(null, netLeashTarget); + leashedComp.Puller = leash; leashedComp.Anchor = anchor; - // I'd like to use a chain joint or smth, but it's too hard and oftentimes buggy - lamia is a good bad example of that. - var joint = CreateLeashJoint(leashedComp.JointId, leash, leashTarget); - var data = new LeashComponent.LeashData(leashedComp.JointId, netLeashTarget); + if (CanCreateJoint(leashTarget, leash)) + { + var jointId = $"leash-joint-{netLeashTarget}"; + var joint = CreateLeashJoint(jointId, leash, leashTarget); + data.JointId = leashedComp.JointId = jointId; + } + else + { + leashedComp.JointId = null; + } if (leash.Comp.LeashSprite is { } sprite) { @@ -411,5 +461,38 @@ public void RemoveLeash(Entity leashed, Entity + /// Refreshes all joints for the specified leash. + /// This will remove all obsolete joints, such as those for which CanCreateJoint returns false, + /// and re-add all joints that were previously removed for the same reason, but became valid later. + /// + public void RefreshJoints(Entity leash) + { + foreach (var data in leash.Comp.Leashed) + { + if (!TryGetEntity(data.Pulled, out var pulled) || !TryComp(pulled, out var leashed)) + continue; + + var shouldExist = CanCreateJoint(pulled.Value, leash); + var exists = data.JointId != null; + + if (exists && !shouldExist && TryComp(pulled, out var jointComp) && jointComp.GetJoints.TryGetValue(data.JointId!, out var joint)) + { + data.JointId = leashed.JointId = null; + _joints.RemoveJoint(joint); + + Log.Debug($"Removed obsolete leash joint between {leash.Owner} and {pulled.Value}"); + } + else if (!exists && shouldExist) + { + var jointId = $"leash-joint-{data.Pulled}"; + joint = CreateLeashJoint(jointId, leash, pulled.Value); + data.JointId = leashed.JointId = jointId; + + Log.Debug($"Added new leash joint between {leash.Owner} and {pulled.Value}"); + } + } + } + #endregion }