Rollerbeds (#5681)
This commit is contained in:
@@ -133,7 +133,7 @@ namespace Content.Server.Buckle.Components
|
||||
break;
|
||||
}
|
||||
|
||||
ownTransform.LocalPosition = Vector2.Zero + BuckleOffset;
|
||||
ownTransform.LocalPosition = Vector2.Zero + strap.BuckleOffset;
|
||||
}
|
||||
|
||||
public bool CanBuckle(EntityUid user, EntityUid to, [NotNullWhen(true)] out StrapComponent? strap)
|
||||
@@ -317,10 +317,17 @@ namespace Content.Server.Buckle.Components
|
||||
|
||||
BuckledTo = null;
|
||||
|
||||
if (_entMan.GetComponent<TransformComponent>(Owner).Parent == _entMan.GetComponent<TransformComponent>(oldBuckledTo.Owner))
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
var xform = entManager.GetComponent<TransformComponent>(Owner);
|
||||
var oldBuckledXform = entManager.GetComponent<TransformComponent>(oldBuckledTo.Owner);
|
||||
|
||||
if (xform.ParentUid == oldBuckledXform.Owner)
|
||||
{
|
||||
_entMan.GetComponent<TransformComponent>(Owner).AttachParentToContainerOrGrid();
|
||||
_entMan.GetComponent<TransformComponent>(Owner).WorldRotation = _entMan.GetComponent<TransformComponent>(oldBuckledTo.Owner).WorldRotation;
|
||||
xform.AttachParentToContainerOrGrid();
|
||||
xform.WorldRotation = oldBuckledXform.WorldRotation;
|
||||
|
||||
if (oldBuckledTo.UnbuckleOffset != Vector2.Zero)
|
||||
xform.Coordinates = oldBuckledXform.Coordinates.Offset(oldBuckledTo.UnbuckleOffset);
|
||||
}
|
||||
|
||||
Appearance?.SetData(BuckleVisuals.Buckled, false);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Acts;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Buckle;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Interaction;
|
||||
@@ -9,6 +11,8 @@ using Content.Shared.Sound;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -21,8 +25,6 @@ namespace Content.Server.Buckle.Components
|
||||
{
|
||||
[ComponentDependency] public readonly SpriteComponent? SpriteComponent = null;
|
||||
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
private readonly HashSet<EntityUid> _buckledEntities = new();
|
||||
|
||||
/// <summary>
|
||||
@@ -37,6 +39,50 @@ namespace Content.Server.Buckle.Components
|
||||
[ViewVariables] [DataField("size")] private int _size = 100;
|
||||
private int _occupiedSize;
|
||||
|
||||
/// <summary>
|
||||
/// 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 <see cref="MaxBuckleDistance"/>
|
||||
/// </summary>
|
||||
[DataField("buckleOffset", required: false)]
|
||||
private Vector2 _buckleOffset = Vector2.Zero;
|
||||
|
||||
private bool _enabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// If disabled, nothing can be buckled on this object, and it will unbuckle anything that's already buckled
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
_enabled = value;
|
||||
if (_enabled == value) return;
|
||||
RemoveAll();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The distance above which a buckled entity will be automatically unbuckled.
|
||||
/// Don't change it unless you really have to
|
||||
/// </summary>
|
||||
[DataField("maxBuckleDistance", required: false)]
|
||||
public float MaxBuckleDistance = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// You can specify the offset the entity will have after unbuckling.
|
||||
/// </summary>
|
||||
[DataField("unbuckleOffset", required: false)]
|
||||
public Vector2 UnbuckleOffset = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Gets and clamps the buckle offset to MaxBuckleDistance
|
||||
/// </summary>
|
||||
public Vector2 BuckleOffset => Vector2.Clamp(
|
||||
_buckleOffset,
|
||||
Vector2.One * -MaxBuckleDistance,
|
||||
Vector2.One * MaxBuckleDistance);
|
||||
|
||||
/// <summary>
|
||||
/// The entity that is currently buckled here, synced from <see cref="BuckleComponent.BuckledTo"/>
|
||||
/// </summary>
|
||||
@@ -96,6 +142,8 @@ namespace Content.Server.Buckle.Components
|
||||
/// <returns>True if added, false otherwise</returns>
|
||||
public bool TryAdd(BuckleComponent buckle, bool force = false)
|
||||
{
|
||||
if (!Enabled) return false;
|
||||
|
||||
if (!force && !HasSpace(buckle))
|
||||
{
|
||||
return false;
|
||||
@@ -110,6 +158,12 @@ namespace Content.Server.Buckle.Components
|
||||
|
||||
buckle.Appearance?.SetData(StrapVisuals.RotationAngle, _rotation);
|
||||
|
||||
// Update the visuals of the strap object
|
||||
if (IoCManager.Resolve<IEntityManager>().TryGetComponent<AppearanceComponent>(Owner, out var appearance))
|
||||
{
|
||||
appearance.SetData("StrapState", true);
|
||||
}
|
||||
|
||||
#pragma warning disable 618
|
||||
SendMessage(new StrapMessage(buckle.Owner, Owner));
|
||||
#pragma warning restore 618
|
||||
@@ -126,6 +180,11 @@ namespace Content.Server.Buckle.Components
|
||||
{
|
||||
if (_buckledEntities.Remove(buckle.Owner))
|
||||
{
|
||||
if (IoCManager.Resolve<IEntityManager>().TryGetComponent<AppearanceComponent>(Owner, out var appearance))
|
||||
{
|
||||
appearance.SetData("StrapState", false);
|
||||
}
|
||||
|
||||
_occupiedSize -= buckle.Size;
|
||||
#pragma warning disable 618
|
||||
SendMessage(new UnStrapMessage(buckle.Owner, Owner));
|
||||
@@ -147,9 +206,11 @@ namespace Content.Server.Buckle.Components
|
||||
|
||||
private void RemoveAll()
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
foreach (var entity in _buckledEntities.ToArray())
|
||||
{
|
||||
if (_entMan.TryGetComponent<BuckleComponent?>(entity, out var buckle))
|
||||
if (entManager.TryGetComponent<BuckleComponent>(entity, out var buckle))
|
||||
{
|
||||
buckle.TryUnbuckle(entity, true);
|
||||
}
|
||||
@@ -166,7 +227,9 @@ namespace Content.Server.Buckle.Components
|
||||
|
||||
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
||||
{
|
||||
if (!_entMan.TryGetComponent<BuckleComponent?>(eventArgs.User, out var buckle))
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entManager.TryGetComponent<BuckleComponent>(eventArgs.User, out var buckle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -176,7 +239,9 @@ namespace Content.Server.Buckle.Components
|
||||
|
||||
public override bool DragDropOn(DragDropEvent eventArgs)
|
||||
{
|
||||
if (!_entMan.TryGetComponent(eventArgs.Dragged, out BuckleComponent? buckleComponent)) return false;
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entManager.TryGetComponent(eventArgs.Dragged, out BuckleComponent? buckleComponent)) return false;
|
||||
return buckleComponent.TryBuckle(eventArgs.User, Owner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +42,12 @@ namespace Content.Server.Buckle.Systems
|
||||
if (!args.CanAccess || !args.CanInteract || !component.Buckled)
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Act = () => component.TryUnbuckle(args.User);
|
||||
verb.Text = Loc.GetString("verb-categories-unbuckle");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png";
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => component.TryUnbuckle(args.User),
|
||||
Text = Loc.GetString("verb-categories-unbuckle"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png"
|
||||
};
|
||||
|
||||
if (args.Target == args.User && args.Using == null)
|
||||
{
|
||||
@@ -81,7 +83,7 @@ namespace Content.Server.Buckle.Systems
|
||||
|
||||
var strapPosition = EntityManager.GetComponent<TransformComponent>(strap.Owner).Coordinates.Offset(buckle.BuckleOffset);
|
||||
|
||||
if (ev.NewPosition.InRange(EntityManager, strapPosition, 0.2f))
|
||||
if (ev.NewPosition.InRange(EntityManager, strapPosition, strap.MaxBuckleDistance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Content.Server.Buckle.Systems
|
||||
|
||||
private void AddStrapVerbs(EntityUid uid, StrapComponent component, GetInteractionVerbsEvent args)
|
||||
{
|
||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
|
||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract || !component.Enabled)
|
||||
return;
|
||||
|
||||
// Note that for whatever bloody reason, buckle component has its own interaction range. Additionally, this
|
||||
@@ -41,9 +41,12 @@ namespace Content.Server.Buckle.Systems
|
||||
if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target, range: buckledComp.Range))
|
||||
continue;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Act = () => buckledComp.TryUnbuckle(args.User);
|
||||
verb.Category = VerbCategory.Unbuckle;
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => buckledComp.TryUnbuckle(args.User),
|
||||
Category = VerbCategory.Unbuckle
|
||||
};
|
||||
|
||||
if (entity == args.User)
|
||||
verb.Text = Loc.GetString("verb-self-target-pronoun");
|
||||
else
|
||||
@@ -64,10 +67,12 @@ namespace Content.Server.Buckle.Systems
|
||||
component.HasSpace(buckle) &&
|
||||
_interactionSystem.InRangeUnobstructed(args.User, args.Target, range: buckle.Range))
|
||||
{
|
||||
Verb verb = new();
|
||||
verb.Act = () => buckle.TryBuckle(args.User, args.Target);
|
||||
verb.Category = VerbCategory.Buckle;
|
||||
verb.Text = Loc.GetString("verb-self-target-pronoun");
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => buckle.TryBuckle(args.User, args.Target),
|
||||
Category = VerbCategory.Buckle,
|
||||
Text = Loc.GetString("verb-self-target-pronoun")
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
@@ -82,14 +87,15 @@ namespace Content.Server.Buckle.Systems
|
||||
if (!_interactionSystem.InRangeUnobstructed(@using, args.Target, usingBuckle.Range, predicate: Ignored))
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Act = () => usingBuckle.TryBuckle(args.User, args.Target);
|
||||
verb.Category = VerbCategory.Buckle;
|
||||
verb.Text = EntityManager.GetComponent<MetaDataComponent>(@using).EntityName;
|
||||
|
||||
// If the used entity is a person being pulled, prioritize this verb. Conversely, if it is
|
||||
// just a held object, the user is probably just trying to sit down.
|
||||
verb.Priority = EntityManager.HasComponent<ActorComponent>(@using) ? 1 : -1;
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => usingBuckle.TryBuckle(args.User, args.Target),
|
||||
Category = VerbCategory.Buckle,
|
||||
Text = EntityManager.GetComponent<MetaDataComponent>(@using).EntityName,
|
||||
// just a held object, the user is probably just trying to sit down.
|
||||
// If the used entity is a person being pulled, prioritize this verb. Conversely, if it is
|
||||
Priority = EntityManager.HasComponent<ActorComponent>(@using) ? 1 : -1
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
25
Content.Server/Foldable/FoldableComponent.cs
Normal file
25
Content.Server/Foldable/FoldableComponent.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
#nullable enable
|
||||
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Foldable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Used to create "foldable structures" that you can pickup like an item when folded. Used for rollerbeds and wheelchairs
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class FoldableComponent : Component
|
||||
{
|
||||
public override string Name => "Foldable";
|
||||
|
||||
[DataField("folded")]
|
||||
[ViewVariables]
|
||||
public bool IsFolded = false;
|
||||
|
||||
public bool CanBeFolded = true;
|
||||
}
|
||||
}
|
||||
131
Content.Server/Foldable/FoldableSystem.cs
Normal file
131
Content.Server/Foldable/FoldableSystem.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Buckle.Components;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Foldable
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class FoldableSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private SharedContainerSystem _container = default!;
|
||||
|
||||
private const string FoldKey = "FoldedState";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<FoldableComponent, ComponentInit>(OnFoldableInit);
|
||||
SubscribeLocalEvent<FoldableComponent, StorageOpenAttemptEvent>(OnFoldableOpenAttempt);
|
||||
SubscribeLocalEvent<FoldableComponent, AttemptItemPickupEvent>(OnPickedUpAttempt);
|
||||
SubscribeLocalEvent<FoldableComponent, GetAlternativeVerbsEvent>(AddFoldVerb);
|
||||
}
|
||||
|
||||
private void OnFoldableOpenAttempt(EntityUid uid, FoldableComponent component, StorageOpenAttemptEvent args)
|
||||
{
|
||||
if (component.IsFolded)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnFoldableInit(EntityUid uid, FoldableComponent component, ComponentInit args)
|
||||
{
|
||||
SetFolded(component, component.IsFolded);
|
||||
}
|
||||
|
||||
private bool TryToggleFold(FoldableComponent comp)
|
||||
{
|
||||
return TrySetFolded(comp, !comp.IsFolded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to fold/unfold
|
||||
/// </summary>
|
||||
/// <param name="comp"></param>
|
||||
/// <param name="state">Folded state we want</param>
|
||||
/// <returns>True if successful</returns>
|
||||
private bool TrySetFolded(FoldableComponent comp, bool state)
|
||||
{
|
||||
if (state == comp.IsFolded)
|
||||
return false;
|
||||
|
||||
if (_container.IsEntityInContainer(comp.Owner))
|
||||
return false;
|
||||
|
||||
// First we check if the foldable object has a strap component
|
||||
if (EntityManager.TryGetComponent(comp.Owner, out StrapComponent? strap))
|
||||
{
|
||||
// If an entity is buckled to the object we can't pick it up or fold it
|
||||
if (strap.BuckledEntities.Any())
|
||||
return false;
|
||||
}
|
||||
|
||||
SetFolded(comp, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the folded state of the given <see cref="FoldableComponent"/>
|
||||
/// </summary>
|
||||
/// <param name="component"></param>
|
||||
/// <param name="folded">If true, the component will become folded, else unfolded</param>
|
||||
private void SetFolded(FoldableComponent component, bool folded)
|
||||
{
|
||||
component.IsFolded = folded;
|
||||
component.CanBeFolded = !_container.IsEntityInContainer(component.Owner);
|
||||
|
||||
// You can't buckle an entity to a folded object
|
||||
if (EntityManager.TryGetComponent(component.Owner, out StrapComponent? strap))
|
||||
strap.Enabled = !component.IsFolded;
|
||||
|
||||
// Update visuals only if the value has changed
|
||||
if (EntityManager.TryGetComponent(component.Owner, out AppearanceComponent? appearance))
|
||||
appearance.SetData(FoldKey, folded);
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
/// <summary>
|
||||
/// Prevents foldable objects to be picked up when unfolded
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="component"></param>
|
||||
/// <param name="args"></param>
|
||||
private void OnPickedUpAttempt(EntityUid uid, FoldableComponent component, AttemptItemPickupEvent args)
|
||||
{
|
||||
if (!component.IsFolded)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Verb
|
||||
|
||||
private void AddFoldVerb(EntityUid uid, FoldableComponent component, GetAlternativeVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => TryToggleFold(component),
|
||||
Text = component.IsFolded ? Loc.GetString("unfold-verb") : Loc.GetString("fold-verb"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/fold.svg.192dpi.png",
|
||||
|
||||
// If the object is unfolded and they click it, they want to fold it, if it's folded, they want to pick it up
|
||||
Priority = component.IsFolded ? 0 : 2,
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -193,12 +193,18 @@ namespace Content.Server.Storage.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
var @event = new StorageOpenAttemptEvent();
|
||||
IoCManager.Resolve<IEntityManager>().EventBus.RaiseLocalEvent(Owner, @event);
|
||||
|
||||
return !@event.Cancelled;
|
||||
}
|
||||
|
||||
public virtual bool CanClose(EntityUid user, bool silent = false)
|
||||
{
|
||||
return true;
|
||||
var @event = new StorageCloseAttemptEvent();
|
||||
IoCManager.Resolve<IEntityManager>().EventBus.RaiseLocalEvent(Owner, @event);
|
||||
|
||||
return !@event.Cancelled;
|
||||
}
|
||||
|
||||
public void ToggleOpen(EntityUid user)
|
||||
@@ -479,4 +485,14 @@ namespace Content.Server.Storage.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class StorageOpenAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public sealed class StorageCloseAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user