Add methods to transfer actions between containers (#20901)

This commit is contained in:
Leon Friedrich
2023-10-12 04:50:10 +11:00
committed by GitHub
parent 8177c406f6
commit fcd0d9ef0f
3 changed files with 98 additions and 17 deletions

View File

@@ -769,10 +769,9 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
{ {
if (_actionsSystem != null && _actionsSystem.TryGetActionData(_menuDragHelper.Dragged?.ActionId, out var action)) if (_actionsSystem != null && _actionsSystem.TryGetActionData(_menuDragHelper.Dragged?.ActionId, out var action))
{ {
if (action.EntityIcon is {} entIcon) if (EntityManager.TryGetComponent(action.EntityIcon, out SpriteComponent? sprite))
{ {
_dragShadow.Texture = EntityManager.GetComponent<SpriteComponent>(entIcon).Icon? _dragShadow.Texture = sprite.Icon?.GetFrame(RsiDirection.South, 0);
.GetFrame(RsiDirection.South, 0);
} }
else if (action.Icon != null) else if (action.Icon != null)
{ {

View File

@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -15,6 +16,7 @@ public sealed class ActionContainerSystem : EntitySystem
[Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly INetManager _netMan = default!; [Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -96,7 +98,61 @@ public sealed class ActionContainerSystem : EntitySystem
} }
/// <summary> /// <summary>
/// Adds a pre-existing action to an action container. /// Transfers an action from one container to another, while keeping the attached entity the same.
/// </summary>
/// <remarks>
/// While the attached entity should be the same at the end, this will actually remove and then re-grant the action.
/// </remarks>
public void TransferAction(
EntityUid actionId,
EntityUid newContainer,
BaseActionComponent? action = null,
ActionsContainerComponent? container = null)
{
if (!_actions.ResolveActionData(actionId, ref action))
return;
if (action.Container == newContainer)
return;
var attached = action.AttachedEntity;
if (!AddAction(newContainer, actionId, action, container))
return;
DebugTools.AssertEqual(action.Container, newContainer);
DebugTools.AssertNull(action.AttachedEntity);
if (attached != null)
_actions.AddActionDirect(attached.Value, actionId, action: action);
DebugTools.AssertEqual(action.AttachedEntity, attached);
}
/// <summary>
/// Transfers all actions from one container to another, while keeping the attached entity the same.
/// </summary>
/// &lt;remarks&gt;
/// While the attached entity should be the same at the end, this will actually remove and then re-grant the action.
/// &lt;/remarks&gt;
public void TransferAllActions(
EntityUid from,
EntityUid to,
ActionsContainerComponent? oldContainer = null,
ActionsContainerComponent? newContainer = null)
{
if (!Resolve(from, ref oldContainer) || !Resolve(to, ref newContainer))
return;
foreach (var action in oldContainer.Container.ContainedEntities.ToArray())
{
TransferAction(action, to, container: newContainer);
}
DebugTools.AssertEqual(oldContainer.Container.Count, 0);
}
/// <summary>
/// Adds a pre-existing action to an action container. If the action is already in some container it will first remove it.
/// </summary> /// </summary>
public bool AddAction(EntityUid uid, EntityUid actionId, BaseActionComponent? action = null, ActionsContainerComponent? comp = null) public bool AddAction(EntityUid uid, EntityUid actionId, BaseActionComponent? action = null, ActionsContainerComponent? comp = null)
{ {
@@ -104,10 +160,7 @@ public sealed class ActionContainerSystem : EntitySystem
return false; return false;
if (action.Container != null) if (action.Container != null)
{ RemoveAction(actionId, action);
Log.Error($"Attempted to insert an action {ToPrettyString(actionId)} that was already in a container {ToPrettyString(action.Container.Value)}");
return false;
}
DebugTools.Assert(comp == null || comp.Owner == uid); DebugTools.Assert(comp == null || comp.Owner == uid);
comp ??= EnsureComp<ActionsContainerComponent>(uid); comp ??= EnsureComp<ActionsContainerComponent>(uid);
@@ -124,6 +177,35 @@ public sealed class ActionContainerSystem : EntitySystem
return true; return true;
} }
/// <summary>
/// Removes an action from its container and any action-performer and moves the action to null-space
/// </summary>
public void RemoveAction(EntityUid actionId, BaseActionComponent? action = null)
{
if (!_actions.ResolveActionData(actionId, ref action))
return;
if (action.Container == null)
return;
_transform.DetachParentToNull(actionId, Transform(actionId));
// Container removal events should have removed the action from the action container.
// However, just in case the container was already deleted we will still manually clear the container field
if (action.Container != null)
{
if (Exists(action.Container))
Log.Error($"Failed to remove action {ToPrettyString(actionId)} from its container {ToPrettyString(action.Container)}?");
action.Container = null;
}
// If the action was granted to some entity, then the removal from the container should have automatically removed it.
// However, if the action was granted without ever being placed in an action container, it will not have been removed.
// Therefore, to ensure that the behaviour of the method is consistent we will also explicitly remove the action.
if (action.AttachedEntity != null)
_actions.RemoveAction(action.AttachedEntity.Value, actionId, action: action);
}
private void OnInit(EntityUid uid, ActionsContainerComponent component, ComponentInit args) private void OnInit(EntityUid uid, ActionsContainerComponent component, ComponentInit args)
{ {
component.Container = _container.EnsureContainer<Container>(uid, ActionsContainerComponent.ContainerId); component.Container = _container.EnsureContainer<Container>(uid, ActionsContainerComponent.ContainerId);
@@ -171,13 +253,7 @@ public sealed class ActionContainerSystem : EntitySystem
var ev = new ActionRemovedEvent(args.Entity, data); var ev = new ActionRemovedEvent(args.Entity, data);
RaiseLocalEvent(uid, ref ev); RaiseLocalEvent(uid, ref ev);
data.Container = null;
if (_netMan.IsServer)
{
// TODO Actions
// log an error or warning here once gibbing code is fixed.
QueueDel(args.Entity);
}
} }
} }

View File

@@ -586,9 +586,16 @@ public abstract class SharedActionsSystem : EntitySystem
if (!ResolveActionData(actionId, ref action)) if (!ResolveActionData(actionId, ref action))
return; return;
if (action.AttachedEntity != performer)
{
Log.Error($"Attempted to remove an action {ToPrettyString(actionId)} from an entity that it was never attached to: {ToPrettyString(performer)}");
return;
}
if (!Resolve(performer, ref comp, false)) if (!Resolve(performer, ref comp, false))
{ {
DebugTools.AssertNull(action.AttachedEntity); DebugTools.Assert(action.AttachedEntity == null || TerminatingOrDeleted(action.AttachedEntity.Value));
action.AttachedEntity = null;
return; return;
} }
@@ -599,7 +606,6 @@ public abstract class SharedActionsSystem : EntitySystem
return; return;
} }
DebugTools.Assert(action.AttachedEntity == performer);
comp.Actions.Remove(actionId.Value); comp.Actions.Remove(actionId.Value);
action.AttachedEntity = null; action.AttachedEntity = null;
Dirty(actionId.Value, action); Dirty(actionId.Value, action);