0 days without buckle breaking (#1366)
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
using Content.Client.GameObjects.Components.Strap;
|
using Content.Client.GameObjects.Components.Strap;
|
||||||
using Content.Client.Interfaces.GameObjects.Components.Interaction;
|
using Content.Client.Interfaces.GameObjects.Components.Interaction;
|
||||||
using Content.Shared.GameObjects.Components.Mobs;
|
using Content.Shared.GameObjects.Components.Buckle;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.GameObjects.Components.Mobs
|
namespace Content.Client.GameObjects.Components.Buckle
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class BuckleComponent : SharedBuckleComponent, IClientDraggable
|
public class BuckleComponent : SharedBuckleComponent, IClientDraggable
|
||||||
@@ -12,7 +12,7 @@ namespace Content.Client.GameObjects.Components.Mobs
|
|||||||
private bool _buckled;
|
private bool _buckled;
|
||||||
private int? _originalDrawDepth;
|
private int? _originalDrawDepth;
|
||||||
|
|
||||||
protected override bool Buckled => _buckled;
|
public override bool Buckled => _buckled;
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Shared.GameObjects.Components.Buckle;
|
||||||
|
using Content.Shared.GameObjects.Components.Strap;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.Animations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.GameObjects.Components.Animations;
|
||||||
|
using Robust.Client.Interfaces.GameObjects.Components;
|
||||||
|
using Robust.Shared.Animations;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Buckle
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class BuckleVisualizer : AppearanceVisualizer
|
||||||
|
{
|
||||||
|
public override void OnChangeData(AppearanceComponent component)
|
||||||
|
{
|
||||||
|
if (!component.TryGetData<bool>(BuckleVisuals.Buckled, out var buckled) ||
|
||||||
|
!buckled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!component.TryGetData<int>(StrapVisuals.RotationAngle, out var angle))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRotation(component, Angle.FromDegrees(angle));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetRotation(AppearanceComponent component, Angle rotation)
|
||||||
|
{
|
||||||
|
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||||
|
|
||||||
|
if (!sprite.Owner.TryGetComponent(out AnimationPlayerComponent animation))
|
||||||
|
{
|
||||||
|
sprite.Rotation = rotation;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animation.HasRunningAnimation("rotate"))
|
||||||
|
{
|
||||||
|
animation.Stop("rotate");
|
||||||
|
}
|
||||||
|
|
||||||
|
animation.Play(new Animation
|
||||||
|
{
|
||||||
|
Length = TimeSpan.FromSeconds(0.125),
|
||||||
|
AnimationTracks =
|
||||||
|
{
|
||||||
|
new AnimationTrackComponentProperty
|
||||||
|
{
|
||||||
|
ComponentType = typeof(ISpriteComponent),
|
||||||
|
Property = nameof(ISpriteComponent.Rotation),
|
||||||
|
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||||
|
KeyFrames =
|
||||||
|
{
|
||||||
|
new AnimationTrackProperty.KeyFrame(sprite.Rotation, 0),
|
||||||
|
new AnimationTrackProperty.KeyFrame(rotation, 0.125f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "rotate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using Content.Shared.GameObjects.Components.Mobs;
|
|
||||||
using Content.Shared.GameObjects.Components.Strap;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
|
|
||||||
namespace Content.Client.GameObjects.Components.Mobs
|
|
||||||
{
|
|
||||||
[UsedImplicitly]
|
|
||||||
public class BuckleVisualizer2D : SpeciesVisualizer2D
|
|
||||||
{
|
|
||||||
public override void OnChangeData(AppearanceComponent component)
|
|
||||||
{
|
|
||||||
if (!component.TryGetData<bool>(SharedBuckleComponent.BuckleVisuals.Buckled, out var buckled) ||
|
|
||||||
!buckled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!component.TryGetData<int>(StrapVisuals.RotationAngle, out var angle))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetRotation(component, Angle.FromDegrees(angle));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,7 +29,7 @@ namespace Content.Client.GameObjects.Components.Mobs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SetRotation(AppearanceComponent component, Angle rotation)
|
private void SetRotation(AppearanceComponent component, Angle rotation)
|
||||||
{
|
{
|
||||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||||
|
|
||||||
|
|||||||
@@ -7,16 +7,5 @@ namespace Content.Client.GameObjects.Components.Strap
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class StrapComponent : SharedStrapComponent
|
public class StrapComponent : SharedStrapComponent
|
||||||
{
|
{
|
||||||
public override StrapPosition Position { get; protected set; }
|
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
|
||||||
{
|
|
||||||
if (!(curState is StrapComponentState strap))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Position = strap.Position;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
154
Content.IntegrationTests/Tests/BuckleTest.cs
Normal file
154
Content.IntegrationTests/Tests/BuckleTest.cs
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.GameObjects.Components.Buckle;
|
||||||
|
using Content.Shared.GameObjects.Components.Buckle;
|
||||||
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Map;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using StrapComponent = Content.Server.GameObjects.Components.Strap.StrapComponent;
|
||||||
|
|
||||||
|
namespace Content.IntegrationTests.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
[TestOf(typeof(BuckleComponent))]
|
||||||
|
[TestOf(typeof(StrapComponent))]
|
||||||
|
public class BuckleTest : ContentIntegrationTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public async Task Test()
|
||||||
|
{
|
||||||
|
var server = StartServerDummyTicker();
|
||||||
|
|
||||||
|
IEntity human = null;
|
||||||
|
IEntity chair = null;
|
||||||
|
BuckleComponent buckle = null;
|
||||||
|
StrapComponent strap = null;
|
||||||
|
|
||||||
|
server.Assert(() =>
|
||||||
|
{
|
||||||
|
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||||
|
|
||||||
|
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||||
|
|
||||||
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
|
||||||
|
human = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace);
|
||||||
|
chair = entityManager.SpawnEntity("ChairWood", MapCoordinates.Nullspace);
|
||||||
|
|
||||||
|
// Default state, unbuckled
|
||||||
|
Assert.True(human.TryGetComponent(out buckle));
|
||||||
|
Assert.NotNull(buckle);
|
||||||
|
Assert.Null(buckle.BuckledTo);
|
||||||
|
Assert.False(buckle.Buckled);
|
||||||
|
Assert.True(ActionBlockerSystem.CanMove(human));
|
||||||
|
Assert.True(ActionBlockerSystem.CanChangeDirection(human));
|
||||||
|
Assert.True(EffectBlockerSystem.CanFall(human));
|
||||||
|
|
||||||
|
// Default state, no buckled entities, strap
|
||||||
|
Assert.True(chair.TryGetComponent(out strap));
|
||||||
|
Assert.NotNull(strap);
|
||||||
|
Assert.IsEmpty(strap.BuckledEntities);
|
||||||
|
Assert.Zero(strap.OccupiedSize);
|
||||||
|
|
||||||
|
// Side effects of buckling
|
||||||
|
Assert.True(buckle.TryBuckle(human, chair));
|
||||||
|
Assert.NotNull(buckle.BuckledTo);
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
Assert.True(((BuckleComponentState) buckle.GetComponentState()).Buckled);
|
||||||
|
Assert.False(ActionBlockerSystem.CanMove(human));
|
||||||
|
Assert.False(ActionBlockerSystem.CanChangeDirection(human));
|
||||||
|
Assert.False(EffectBlockerSystem.CanFall(human));
|
||||||
|
Assert.AreEqual(human.Transform.WorldPosition, chair.Transform.WorldPosition);
|
||||||
|
|
||||||
|
// Side effects of buckling for the strap
|
||||||
|
Assert.That(strap.BuckledEntities, Does.Contain(human));
|
||||||
|
Assert.That(strap.OccupiedSize, Is.EqualTo(buckle.Size));
|
||||||
|
Assert.Positive(strap.OccupiedSize);
|
||||||
|
|
||||||
|
// Trying to buckle while already buckled fails
|
||||||
|
Assert.False(buckle.TryBuckle(human, chair));
|
||||||
|
|
||||||
|
// Trying to unbuckle too quickly fails
|
||||||
|
Assert.False(buckle.TryUnbuckle(human));
|
||||||
|
Assert.False(buckle.ToggleBuckle(human, chair));
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait enough ticks for the unbuckling cooldown to run out
|
||||||
|
server.RunTicks(60);
|
||||||
|
|
||||||
|
server.Assert(() =>
|
||||||
|
{
|
||||||
|
// Still buckled
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
|
||||||
|
// Unbuckle
|
||||||
|
Assert.True(buckle.TryUnbuckle(human));
|
||||||
|
Assert.Null(buckle.BuckledTo);
|
||||||
|
Assert.False(buckle.Buckled);
|
||||||
|
Assert.True(ActionBlockerSystem.CanMove(human));
|
||||||
|
Assert.True(ActionBlockerSystem.CanChangeDirection(human));
|
||||||
|
Assert.True(EffectBlockerSystem.CanFall(human));
|
||||||
|
|
||||||
|
// Unbuckle, strap
|
||||||
|
Assert.IsEmpty(strap.BuckledEntities);
|
||||||
|
Assert.Zero(strap.OccupiedSize);
|
||||||
|
|
||||||
|
// Re-buckling has no cooldown
|
||||||
|
Assert.True(buckle.TryBuckle(human, chair));
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
|
||||||
|
// On cooldown
|
||||||
|
Assert.False(buckle.TryUnbuckle(human));
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
Assert.False(buckle.ToggleBuckle(human, chair));
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
Assert.False(buckle.ToggleBuckle(human, chair));
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait enough ticks for the unbuckling cooldown to run out
|
||||||
|
server.RunTicks(60);
|
||||||
|
|
||||||
|
server.Assert(() =>
|
||||||
|
{
|
||||||
|
// Still buckled
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
|
||||||
|
// Unbuckle
|
||||||
|
Assert.True(buckle.TryUnbuckle(human));
|
||||||
|
Assert.False(buckle.Buckled);
|
||||||
|
|
||||||
|
// Move away from the chair
|
||||||
|
human.Transform.WorldPosition += (1000, 1000);
|
||||||
|
|
||||||
|
// Out of range
|
||||||
|
Assert.False(buckle.TryBuckle(human, chair));
|
||||||
|
Assert.False(buckle.TryUnbuckle(human));
|
||||||
|
Assert.False(buckle.ToggleBuckle(human, chair));
|
||||||
|
|
||||||
|
// Move near the chair
|
||||||
|
human.Transform.WorldPosition = chair.Transform.WorldPosition + (0.5f, 0);
|
||||||
|
|
||||||
|
// In range
|
||||||
|
Assert.True(buckle.TryBuckle(human, chair));
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
Assert.False(buckle.TryUnbuckle(human));
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
Assert.False(buckle.ToggleBuckle(human, chair));
|
||||||
|
Assert.True(buckle.Buckled);
|
||||||
|
|
||||||
|
// Force unbuckle
|
||||||
|
Assert.True(buckle.TryUnbuckle(human, true));
|
||||||
|
Assert.False(buckle.Buckled);
|
||||||
|
Assert.True(ActionBlockerSystem.CanMove(human));
|
||||||
|
Assert.True(ActionBlockerSystem.CanChangeDirection(human));
|
||||||
|
Assert.True(EffectBlockerSystem.CanFall(human));
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using System;
|
using System;
|
||||||
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
using Content.Server.GameObjects.Components.Strap;
|
using Content.Server.GameObjects.Components.Strap;
|
||||||
using Content.Server.Interfaces;
|
using Content.Server.Interfaces;
|
||||||
using Content.Server.Interfaces.GameObjects.Components.Interaction;
|
using Content.Server.Interfaces.GameObjects.Components.Interaction;
|
||||||
using Content.Server.Mobs;
|
using Content.Server.Mobs;
|
||||||
using Content.Server.Utility;
|
using Content.Server.Utility;
|
||||||
using Content.Shared.GameObjects;
|
using Content.Shared.GameObjects;
|
||||||
|
using Content.Shared.GameObjects.Components.Buckle;
|
||||||
using Content.Shared.GameObjects.Components.Mobs;
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
using Content.Shared.GameObjects.Components.Strap;
|
using Content.Shared.GameObjects.Components.Strap;
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
@@ -22,7 +24,7 @@ using Robust.Shared.Maths;
|
|||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Mobs
|
namespace Content.Server.GameObjects.Components.Buckle
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class BuckleComponent : SharedBuckleComponent, IInteractHand, IDragDrop
|
public class BuckleComponent : SharedBuckleComponent, IInteractHand, IDragDrop
|
||||||
@@ -34,29 +36,32 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of space that this entity occupies in a <see cref="StrapComponent"/>.
|
|
||||||
/// </summary>
|
|
||||||
private int _size;
|
private int _size;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The range from which this entity can buckle to a <see cref="StrapComponent"/>.
|
/// The range from which this entity can buckle to a <see cref="StrapComponent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
private float _range;
|
private float _range;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of time that must pass for this entity to
|
/// The amount of time that must pass for this entity to
|
||||||
/// be able to unbuckle after recently buckling.
|
/// be able to unbuckle after recently buckling.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
private TimeSpan _unbuckleDelay;
|
private TimeSpan _unbuckleDelay;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time that this entity buckled at.
|
/// The time that this entity buckled at.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
private TimeSpan _buckleTime;
|
private TimeSpan _buckleTime;
|
||||||
|
|
||||||
private StrapComponent? _buckledTo;
|
private StrapComponent? _buckledTo;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The strap that this component is buckled to.
|
||||||
|
/// </summary>
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public StrapComponent? BuckledTo
|
public StrapComponent? BuckledTo
|
||||||
{
|
{
|
||||||
@@ -70,13 +75,25 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
}
|
}
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
protected override bool Buckled => BuckledTo != null;
|
public override bool Buckled => BuckledTo != null;
|
||||||
|
|
||||||
public bool ContainerChanged { get; private set; }
|
/// <summary>
|
||||||
|
/// True if the entity was inserted or removed from a container
|
||||||
|
/// before updating, false otherwise.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
private bool ContainerChanged { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of space that this entity occupies in a <see cref="StrapComponent"/>.
|
||||||
|
/// </summary>
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public int Size => _size;
|
public int Size => _size;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows or hides the buckled status effect depending on if the
|
||||||
|
/// entity is buckled or not.
|
||||||
|
/// </summary>
|
||||||
private void BuckleStatus()
|
private void BuckleStatus()
|
||||||
{
|
{
|
||||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status))
|
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||||
@@ -88,6 +105,10 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reattaches this entity to the strap, modifying its position and rotation
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strap">The strap to reattach to</param>
|
||||||
private void ReAttach(StrapComponent strap)
|
private void ReAttach(StrapComponent strap)
|
||||||
{
|
{
|
||||||
var ownTransform = Owner.Transform;
|
var ownTransform = Owner.Transform;
|
||||||
@@ -112,7 +133,18 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryBuckle(IEntity user, IEntity to)
|
/// <summary>
|
||||||
|
/// Tries to make an entity buckle the owner of this component to another.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">
|
||||||
|
/// The entity buckling the owner of this component, can be the owner itself.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="to">The entity to buckle the owner of this component to.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// true if the owner was buckled, otherwise false even if the owner was
|
||||||
|
/// previously already buckled.
|
||||||
|
/// </returns>
|
||||||
|
public bool TryBuckle(IEntity user, IEntity to)
|
||||||
{
|
{
|
||||||
if (user == null || user == to)
|
if (user == null || user == to)
|
||||||
{
|
{
|
||||||
@@ -142,8 +174,10 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If in a container
|
||||||
if (ContainerHelpers.TryGetContainer(Owner, out var ownerContainer))
|
if (ContainerHelpers.TryGetContainer(Owner, out var ownerContainer))
|
||||||
{
|
{
|
||||||
|
// And not in the same container as the strap
|
||||||
if (!ContainerHelpers.TryGetContainer(strap.Owner, out var strapContainer) ||
|
if (!ContainerHelpers.TryGetContainer(strap.Owner, out var strapContainer) ||
|
||||||
ownerContainer != strapContainer)
|
ownerContainer != strapContainer)
|
||||||
{
|
{
|
||||||
@@ -218,6 +252,18 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to unbuckle the Owner of this component from its current strap.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The entity doing the unbuckling.</param>
|
||||||
|
/// <param name="force">
|
||||||
|
/// Whether to force the unbuckling or not. Does not guarantee true to
|
||||||
|
/// be returned, but guarantees the owner to be unbuckled afterwards.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// true if the owner was unbuckled, otherwise false even if the owner
|
||||||
|
/// was previously already unbuckled.
|
||||||
|
/// </returns>
|
||||||
public bool TryUnbuckle(IEntity user, bool force = false)
|
public bool TryUnbuckle(IEntity user, bool force = false)
|
||||||
{
|
{
|
||||||
if (BuckledTo == null)
|
if (BuckledTo == null)
|
||||||
@@ -288,16 +334,35 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ToggleBuckle(IEntity user, IEntity to)
|
/// <summary>
|
||||||
|
/// Makes an entity toggle the buckling status of the owner to a
|
||||||
|
/// specific entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The entity doing the buckling/unbuckling.</param>
|
||||||
|
/// <param name="to">
|
||||||
|
/// The entity to toggle the buckle status of the owner to.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="force">
|
||||||
|
/// Whether to force the unbuckling or not, if it happens. Does not
|
||||||
|
/// guarantee true to be returned, but guarantees the owner to be
|
||||||
|
/// unbuckled afterwards.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>true if the buckling status was changed, false otherwise.</returns>
|
||||||
|
public bool ToggleBuckle(IEntity user, IEntity to, bool force = false)
|
||||||
{
|
{
|
||||||
if (BuckledTo?.Owner == to)
|
if (BuckledTo?.Owner == to)
|
||||||
{
|
{
|
||||||
return TryUnbuckle(user);
|
return TryUnbuckle(user, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TryBuckle(user, to);
|
return TryBuckle(user, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the owner is inserted or removed from a container,
|
||||||
|
/// to synchronize the state of buckling.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">The message received</param>
|
||||||
private void InsertIntoContainer(ContainerModifiedMessage message)
|
private void InsertIntoContainer(ContainerModifiedMessage message)
|
||||||
{
|
{
|
||||||
if (message.Entity != Owner)
|
if (message.Entity != Owner)
|
||||||
@@ -308,6 +373,12 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
ContainerChanged = true;
|
ContainerChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronizes the state of buckling depending on whether the entity
|
||||||
|
/// was inserted or removed from a container, and whether or not
|
||||||
|
/// its current strap (if there is one) has also been put into or removed
|
||||||
|
/// from the same container as well.
|
||||||
|
/// </summary>
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
if (!ContainerChanged || BuckledTo == null)
|
if (!ContainerChanged || BuckledTo == null)
|
||||||
@@ -401,6 +472,10 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
return TryBuckle(eventArgs.User, eventArgs.Target);
|
return TryBuckle(eventArgs.User, eventArgs.Target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows the unbuckling of the owning entity through a verb if
|
||||||
|
/// anyone right clicks them.
|
||||||
|
/// </summary>
|
||||||
[Verb]
|
[Verb]
|
||||||
private sealed class BuckleVerb : Verb<BuckleComponent>
|
private sealed class BuckleVerb : Verb<BuckleComponent>
|
||||||
{
|
{
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Content.Server.GameObjects.Components.Buckle;
|
||||||
using Content.Shared.GameObjects.Components.Mobs;
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
using Content.Server.GameObjects.Components.Buckle;
|
||||||
using Content.Server.Interfaces.GameObjects.Components.Interaction;
|
using Content.Server.Interfaces.GameObjects.Components.Interaction;
|
||||||
using Content.Shared.GameObjects;
|
using Content.Shared.GameObjects;
|
||||||
using Content.Shared.GameObjects.Components.Strap;
|
using Content.Shared.GameObjects.Components.Strap;
|
||||||
@@ -17,30 +17,34 @@ namespace Content.Server.GameObjects.Components.Strap
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class StrapComponent : SharedStrapComponent, IInteractHand
|
public class StrapComponent : SharedStrapComponent, IInteractHand
|
||||||
{
|
{
|
||||||
|
private HashSet<IEntity> _buckledEntities;
|
||||||
private StrapPosition _position;
|
private StrapPosition _position;
|
||||||
private string _buckleSound;
|
private string _buckleSound;
|
||||||
private string _unbuckleSound;
|
private string _unbuckleSound;
|
||||||
private string _buckledIcon;
|
private string _buckledIcon;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The angle in degrees to rotate the player by when they get strapped
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
private int _rotation;
|
private int _rotation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the strap which is compared against when buckling entities
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
private int _size;
|
private int _size;
|
||||||
|
private int _occupiedSize;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entity that is currently buckled here, synced from <see cref="BuckleComponent.BuckledTo"/>
|
/// The entity that is currently buckled here, synced from <see cref="BuckleComponent.BuckledTo"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private HashSet<IEntity> BuckledEntities { get; set; }
|
public IReadOnlyCollection<IEntity> BuckledEntities => _buckledEntities;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The change in position to the strapped mob
|
/// The change in position to the strapped mob
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override StrapPosition Position
|
public StrapPosition Position => _position;
|
||||||
{
|
|
||||||
get => _position;
|
|
||||||
protected set
|
|
||||||
{
|
|
||||||
_position = value;
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sound to be played when a mob is buckled
|
/// The sound to be played when a mob is buckled
|
||||||
@@ -60,34 +64,29 @@ namespace Content.Server.GameObjects.Components.Strap
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public string BuckledIcon => _buckledIcon;
|
public string BuckledIcon => _buckledIcon;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The angle in degrees to rotate the player by when they get strapped
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public int Rotation => _rotation;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The size of the strap which is compared against when buckling entities
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public int Size => _size;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sum of the sizes of all the buckled entities in this strap
|
/// The sum of the sizes of all the buckled entities in this strap
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
private int OccupiedSize { get; set; }
|
public int OccupiedSize => _occupiedSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this strap has enough space for a new occupant.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buckle">The new occupant</param>
|
||||||
|
/// <returns>true if there is enough space, false otherwise</returns>
|
||||||
public bool HasSpace(BuckleComponent buckle)
|
public bool HasSpace(BuckleComponent buckle)
|
||||||
{
|
{
|
||||||
return OccupiedSize + buckle.Size <= _size;
|
return OccupiedSize + buckle.Size <= _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a buckled entity. Called from <see cref="BuckleComponent.TryBuckle"/>
|
/// Adds a buckled entity. Called from <see cref="BuckleComponent.TryBuckle"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="buckle">The component to add</param>
|
/// <param name="buckle">The component to add</param>
|
||||||
/// <param name="force">Whether or not to check if the strap has enough space</param>
|
/// <param name="force">
|
||||||
|
/// Whether or not to check if the strap has enough space
|
||||||
|
/// </param>
|
||||||
/// <returns>True if added, false otherwise</returns>
|
/// <returns>True if added, false otherwise</returns>
|
||||||
public bool TryAdd(BuckleComponent buckle, bool force = false)
|
public bool TryAdd(BuckleComponent buckle, bool force = false)
|
||||||
{
|
{
|
||||||
@@ -96,12 +95,12 @@ namespace Content.Server.GameObjects.Components.Strap
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!BuckledEntities.Add(buckle.Owner))
|
if (!_buckledEntities.Add(buckle.Owner))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OccupiedSize += buckle.Size;
|
_occupiedSize += buckle.Size;
|
||||||
|
|
||||||
if (buckle.Owner.TryGetComponent(out AppearanceComponent appearance))
|
if (buckle.Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||||
{
|
{
|
||||||
@@ -112,14 +111,15 @@ namespace Content.Server.GameObjects.Components.Strap
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a buckled entity. Called from <see cref="BuckleComponent.TryUnbuckle"/>
|
/// Removes a buckled entity.
|
||||||
|
/// Called from <see cref="BuckleComponent.TryUnbuckle"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="buckle">The component to remove</param>
|
/// <param name="buckle">The component to remove</param>
|
||||||
public void Remove(BuckleComponent buckle)
|
public void Remove(BuckleComponent buckle)
|
||||||
{
|
{
|
||||||
if (BuckledEntities.Remove(buckle.Owner))
|
if (_buckledEntities.Remove(buckle.Owner))
|
||||||
{
|
{
|
||||||
OccupiedSize -= buckle.Size;
|
_occupiedSize -= buckle.Size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,16 +136,16 @@ namespace Content.Server.GameObjects.Components.Strap
|
|||||||
var defaultSize = 100;
|
var defaultSize = 100;
|
||||||
|
|
||||||
serializer.DataField(ref _size, "size", defaultSize);
|
serializer.DataField(ref _size, "size", defaultSize);
|
||||||
BuckledEntities = new HashSet<IEntity>(_size / defaultSize);
|
_buckledEntities = new HashSet<IEntity>(_size / defaultSize);
|
||||||
|
|
||||||
OccupiedSize = 0;
|
_occupiedSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnRemove()
|
public override void OnRemove()
|
||||||
{
|
{
|
||||||
base.OnRemove();
|
base.OnRemove();
|
||||||
|
|
||||||
foreach (var entity in BuckledEntities)
|
foreach (var entity in _buckledEntities)
|
||||||
{
|
{
|
||||||
if (entity.TryGetComponent(out BuckleComponent buckle))
|
if (entity.TryGetComponent(out BuckleComponent buckle))
|
||||||
{
|
{
|
||||||
@@ -153,13 +153,8 @@ namespace Content.Server.GameObjects.Components.Strap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BuckledEntities.Clear();
|
_buckledEntities.Clear();
|
||||||
OccupiedSize = 0;
|
_occupiedSize = 0;
|
||||||
}
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState()
|
|
||||||
{
|
|
||||||
return new StrapComponentState(Position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.GameObjects.Components.Mobs;
|
using Content.Server.GameObjects.Components.Buckle;
|
||||||
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
using Content.Server.GameObjects.EntitySystems.Click;
|
using Content.Server.GameObjects.EntitySystems.Click;
|
||||||
using Robust.Server.GameObjects.EntitySystems;
|
using Robust.Server.GameObjects.EntitySystems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Content.Shared.GameObjects.EntitySystems;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.GameObjects.Components.Mobs
|
namespace Content.Shared.GameObjects.Components.Buckle
|
||||||
{
|
{
|
||||||
public abstract class SharedBuckleComponent : Component, IActionBlocker, IEffectBlocker
|
public abstract class SharedBuckleComponent : Component, IActionBlocker, IEffectBlocker
|
||||||
{
|
{
|
||||||
@@ -11,7 +11,10 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
|||||||
|
|
||||||
public sealed override uint? NetID => ContentNetIDs.BUCKLE;
|
public sealed override uint? NetID => ContentNetIDs.BUCKLE;
|
||||||
|
|
||||||
protected abstract bool Buckled { get; }
|
/// <summary>
|
||||||
|
/// True if the entity is buckled, false otherwise.
|
||||||
|
/// </summary>
|
||||||
|
public abstract bool Buckled { get; }
|
||||||
|
|
||||||
bool IActionBlocker.CanMove()
|
bool IActionBlocker.CanMove()
|
||||||
{
|
{
|
||||||
@@ -27,24 +30,24 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
|||||||
{
|
{
|
||||||
return !Buckled;
|
return !Buckled;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
protected sealed class BuckleComponentState : ComponentState
|
public sealed class BuckleComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public BuckleComponentState(bool buckled, int? drawDepth) : base(ContentNetIDs.BUCKLE)
|
||||||
{
|
{
|
||||||
public BuckleComponentState(bool buckled, int? drawDepth) : base(ContentNetIDs.BUCKLE)
|
Buckled = buckled;
|
||||||
{
|
DrawDepth = drawDepth;
|
||||||
Buckled = buckled;
|
|
||||||
DrawDepth = drawDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Buckled { get; }
|
|
||||||
public int? DrawDepth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
public bool Buckled { get; }
|
||||||
public enum BuckleVisuals
|
public int? DrawDepth;
|
||||||
{
|
}
|
||||||
Buckled
|
|
||||||
}
|
[Serializable, NetSerializable]
|
||||||
|
public enum BuckleVisuals
|
||||||
|
{
|
||||||
|
Buckled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,21 +27,6 @@ namespace Content.Shared.GameObjects.Components.Strap
|
|||||||
public sealed override string Name => "Strap";
|
public sealed override string Name => "Strap";
|
||||||
|
|
||||||
public sealed override uint? NetID => ContentNetIDs.STRAP;
|
public sealed override uint? NetID => ContentNetIDs.STRAP;
|
||||||
|
|
||||||
public abstract StrapPosition Position { get; protected set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class StrapComponentState : ComponentState
|
|
||||||
{
|
|
||||||
public readonly StrapPosition Position;
|
|
||||||
|
|
||||||
public StrapComponentState(StrapPosition position) : base(ContentNetIDs.BUCKLE)
|
|
||||||
{
|
|
||||||
Position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Buckled { get; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
|
|||||||
@@ -127,7 +127,7 @@
|
|||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: SpeciesVisualizer2D
|
- type: SpeciesVisualizer2D
|
||||||
- type: BuckleVisualizer2D
|
- type: BuckleVisualizer
|
||||||
- type: CombatMode
|
- type: CombatMode
|
||||||
- type: Teleportable
|
- type: Teleportable
|
||||||
- type: CharacterInfo
|
- type: CharacterInfo
|
||||||
|
|||||||
Reference in New Issue
Block a user