Retractable items system + Arm Blade action (#38150)
This commit is contained in:
@@ -72,7 +72,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
continue;
|
||||
|
||||
var item = Spawn(slot.StartingItem, Transform(uid).Coordinates);
|
||||
|
||||
|
||||
if (slot.ContainerSlot != null)
|
||||
_containers.Insert(item, slot.ContainerSlot);
|
||||
}
|
||||
|
||||
@@ -124,6 +124,27 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to pick up an entity into a hand, forcing to drop an item if its not free.
|
||||
/// By default it does check if it's possible to drop items.
|
||||
/// </summary>
|
||||
public bool TryForcePickup(
|
||||
EntityUid uid,
|
||||
EntityUid entity,
|
||||
Hand hand,
|
||||
bool checkActionBlocker = true,
|
||||
bool animate = true,
|
||||
HandsComponent? handsComp = null,
|
||||
ItemComponent? item = null)
|
||||
{
|
||||
if (!Resolve(uid, ref handsComp, false))
|
||||
return false;
|
||||
|
||||
TryDrop(uid, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp);
|
||||
|
||||
return TryPickup(uid, entity, hand, checkActionBlocker, animate, handsComp, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to pick up an entity into any hand, forcing to drop an item if there are no free hands
|
||||
/// By default it does check if it's possible to drop items
|
||||
|
||||
@@ -215,7 +215,10 @@ namespace Content.Shared.Interaction
|
||||
/// </summary>
|
||||
private void OnRemoveAttempt(EntityUid uid, UnremoveableComponent item, ContainerGettingRemovedAttemptEvent args)
|
||||
{
|
||||
args.Cancel();
|
||||
// don't prevent the server state for the container from being applied to the client correctly
|
||||
// otherwise this will cause an error if the client predicts adding UnremoveableComponent
|
||||
if (!_gameTiming.ApplyingState)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.RetractableItemAction;
|
||||
|
||||
/// <summary>
|
||||
/// Component used as a marker for items summoned by the RetractableItemAction system.
|
||||
/// Used for keeping track of items summoned by said action.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(RetractableItemActionSystem))]
|
||||
public sealed partial class ActionRetractableItemComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The action that marked this item.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? SummoningAction;
|
||||
}
|
||||
9
Content.Shared/RetractableItemAction/ItemActionEvents.cs
Normal file
9
Content.Shared/RetractableItemAction/ItemActionEvents.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared.RetractableItemAction;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when using the RetractableItem action.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public sealed partial class OnRetractableItemActionEvent : InstantActionEvent;
|
||||
@@ -0,0 +1,41 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.RetractableItemAction;
|
||||
|
||||
/// <summary>
|
||||
/// Used for storing an unremovable item within an action and summoning it into your hand on use.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(RetractableItemActionSystem))]
|
||||
public sealed partial class RetractableItemActionComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The item that will appear be spawned by the action.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public EntProtoId SpawnedPrototype;
|
||||
|
||||
/// <summary>
|
||||
/// Sound collection to play when the item is summoned.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundCollectionSpecifier? SummonSounds;
|
||||
|
||||
/// <summary>
|
||||
/// Sound collection to play when the summoned item is retracted back into the action.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundCollectionSpecifier? RetractSounds;
|
||||
|
||||
/// <summary>
|
||||
/// The item managed by the action. Will be summoned and hidden as the action is used.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? ActionItemUid;
|
||||
|
||||
/// <summary>
|
||||
/// The container ID used to store the item.
|
||||
/// </summary>
|
||||
public const string ContainerId = "item-action-item-container";
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Shared.RetractableItemAction;
|
||||
|
||||
/// <summary>
|
||||
/// System for handling retractable items, such as armblades.
|
||||
/// </summary>
|
||||
public sealed class RetractableItemActionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containers = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popups = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RetractableItemActionComponent, MapInitEvent>(OnActionInit);
|
||||
SubscribeLocalEvent<RetractableItemActionComponent, OnRetractableItemActionEvent>(OnRetractableItemAction);
|
||||
|
||||
SubscribeLocalEvent<ActionRetractableItemComponent, ComponentShutdown>(OnActionSummonedShutdown);
|
||||
}
|
||||
|
||||
private void OnActionInit(Entity<RetractableItemActionComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
_containers.EnsureContainer<Container>(ent, RetractableItemActionComponent.ContainerId);
|
||||
|
||||
PopulateActionItem(ent.Owner);
|
||||
}
|
||||
|
||||
private void OnRetractableItemAction(Entity<RetractableItemActionComponent> ent, ref OnRetractableItemActionEvent args)
|
||||
{
|
||||
if (_hands.GetActiveHand(args.Performer) is not { } userHand)
|
||||
return;
|
||||
|
||||
if (_actions.GetAction(ent.Owner) is not { } action)
|
||||
return;
|
||||
|
||||
if (action.Comp.AttachedEntity == null)
|
||||
return;
|
||||
|
||||
if (ent.Comp.ActionItemUid == null)
|
||||
return;
|
||||
|
||||
// Don't allow to summon an item if holding an unremoveable item unless that item is summoned by the action.
|
||||
if (userHand.HeldEntity != null && !_hands.IsHolding(args.Performer, ent.Comp.ActionItemUid) && !_hands.CanDropHeld(args.Performer, userHand, false))
|
||||
{
|
||||
_popups.PopupClient(Loc.GetString("retractable-item-hand-cannot-drop"), args.Performer, args.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_hands.IsHolding(args.Performer, ent.Comp.ActionItemUid))
|
||||
{
|
||||
RemComp<UnremoveableComponent>(ent.Comp.ActionItemUid.Value);
|
||||
var container = _containers.GetContainer(ent, RetractableItemActionComponent.ContainerId);
|
||||
_containers.Insert(ent.Comp.ActionItemUid.Value, container);
|
||||
_audio.PlayPredicted(ent.Comp.RetractSounds, action.Comp.AttachedEntity.Value, action.Comp.AttachedEntity.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_hands.TryForcePickup(args.Performer, ent.Comp.ActionItemUid.Value, userHand, checkActionBlocker: false);
|
||||
_audio.PlayPredicted(ent.Comp.SummonSounds, action.Comp.AttachedEntity.Value, action.Comp.AttachedEntity.Value);
|
||||
EnsureComp<UnremoveableComponent>(ent.Comp.ActionItemUid.Value);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnActionSummonedShutdown(Entity<ActionRetractableItemComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (_actions.GetAction(ent.Comp.SummoningAction) is not { } action)
|
||||
return;
|
||||
|
||||
if (!TryComp<RetractableItemActionComponent>(action, out var retract) || retract.ActionItemUid != ent.Owner)
|
||||
return;
|
||||
|
||||
// If the item is somehow destroyed, re-add it to the action.
|
||||
PopulateActionItem(action.Owner);
|
||||
}
|
||||
|
||||
private void PopulateActionItem(Entity<RetractableItemActionComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent.Owner, ref ent.Comp, false) || TerminatingOrDeleted(ent))
|
||||
return;
|
||||
|
||||
if (!PredictedTrySpawnInContainer(ent.Comp.SpawnedPrototype, ent.Owner, RetractableItemActionComponent.ContainerId, out var summoned))
|
||||
return;
|
||||
|
||||
ent.Comp.ActionItemUid = summoned.Value;
|
||||
|
||||
// Mark the unremovable item so it can be added back into the action.
|
||||
var summonedComp = AddComp<ActionRetractableItemComponent>(summoned.Value);
|
||||
summonedComp.SummoningAction = ent.Owner;
|
||||
Dirty(summoned.Value, summonedComp);
|
||||
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user