Improve stack merging and crafting (#7105)

This commit is contained in:
Leon Friedrich
2022-03-28 17:03:14 +13:00
committed by GitHub
parent 50ea06ba72
commit 51b1535255
6 changed files with 149 additions and 82 deletions

View File

@@ -1,23 +1,28 @@
using Content.Shared.Stacks; using Content.Shared.Stacks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Client.Stack namespace Content.Client.Stack
{ {
[UsedImplicitly] [UsedImplicitly]
public sealed class StackSystem : SharedStackSystem public sealed class StackSystem : SharedStackSystem
{ {
public override void Initialize() public override void SetCount(EntityUid uid, int amount, SharedStackComponent? component = null)
{ {
base.Initialize(); if (!Resolve(uid, ref component))
return;
SubscribeLocalEvent<StackComponent, StackCountChangedEvent>(OnStackCountChanged); base.SetCount(uid, amount, component);
// TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call.
if (component.Count <= 0)
{
Transform(uid).DetachParentToNull();
return;
} }
private void OnStackCountChanged(EntityUid uid, StackComponent component, StackCountChangedEvent args)
{
// Dirty the UI now that the stack count has changed. // Dirty the UI now that the stack count has changed.
component.UiUpdateNeeded = true; if (component is StackComponent clientComp)
clientComp.UiUpdateNeeded = true;
} }
} }
} }

View File

@@ -14,6 +14,7 @@ using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Stacks;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -336,8 +337,12 @@ namespace Content.Server.Construction
} }
} }
if (await Construct(user, "item_construction", constructionGraph, edge, targetNode) is {Valid: true} item) if (await Construct(user, "item_construction", constructionGraph, edge, targetNode) is not { Valid: true } item)
_handsSystem.PickupOrDrop(user, item); return;
// Just in case this is a stack, attempt to merge it. If it isn't a stack, this will just normally pick up
// or drop the item as normal.
_stackSystem.TryMergeToHands(item, user);
} }
// LEGACY CODE. See warning at the top of the file! // LEGACY CODE. See warning at the top of the file!

View File

@@ -1,17 +1,7 @@
using System;
using Content.Server.Hands.Components;
using Content.Server.Popups;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.Stacks; using Content.Shared.Stacks;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -25,8 +15,6 @@ namespace Content.Server.Stack
public sealed class StackSystem : SharedStackSystem public sealed class StackSystem : SharedStackSystem
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public static readonly int[] DefaultSplitAmounts = { 1, 5, 10, 20, 30, 50 }; public static readonly int[] DefaultSplitAmounts = { 1, 5, 10, 20, 30, 50 };
@@ -34,10 +22,21 @@ namespace Content.Server.Stack
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<StackComponent, InteractUsingEvent>(OnStackInteractUsing);
SubscribeLocalEvent<StackComponent, GetVerbsEvent<AlternativeVerb>>(OnStackAlternativeInteract); SubscribeLocalEvent<StackComponent, GetVerbsEvent<AlternativeVerb>>(OnStackAlternativeInteract);
} }
public override void SetCount(EntityUid uid, int amount, SharedStackComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
base.SetCount(uid, amount, component);
// Queue delete stack if count reaches zero.
if (component.Count <= 0)
QueueDel(uid);
}
/// <summary> /// <summary>
/// Try to split this stack into two. Returns a non-null <see cref="Robust.Shared.GameObjects.EntityUid"/> if successful. /// Try to split this stack into two. Returns a non-null <see cref="Robust.Shared.GameObjects.EntityUid"/> if successful.
/// </summary> /// </summary>
@@ -83,51 +82,6 @@ namespace Content.Server.Stack
return entity; return entity;
} }
private void OnStackInteractUsing(EntityUid uid, StackComponent stack, InteractUsingEvent args)
{
if (args.Handled)
return;
if (!TryComp<StackComponent>(args.Used, out var otherStack))
return;
if (!otherStack.StackTypeId.Equals(stack.StackTypeId))
return;
var toTransfer = Math.Min(stack.Count, otherStack.AvailableSpace);
SetCount(uid, stack.Count - toTransfer, stack);
SetCount(args.Used, otherStack.Count + toTransfer, otherStack);
var popupPos = args.ClickLocation;
if (!popupPos.IsValid(EntityManager))
{
popupPos = Transform(args.User).Coordinates;
}
var filter = Filter.Entities(args.User);
switch (toTransfer)
{
case > 0:
_popupSystem.PopupCoordinates($"+{toTransfer}", popupPos, filter);
if (otherStack.AvailableSpace == 0)
{
_popupSystem.PopupCoordinates(Loc.GetString("comp-stack-becomes-full"),
popupPos.Offset(new Vector2(0, -0.5f)) , filter);
}
break;
case 0 when otherStack.AvailableSpace == 0:
_popupSystem.PopupCoordinates(Loc.GetString("comp-stack-already-full"), popupPos, filter);
break;
}
args.Handled = true;
}
private void OnStackAlternativeInteract(EntityUid uid, StackComponent stack, GetVerbsEvent<AlternativeVerb> args) private void OnStackAlternativeInteract(EntityUid uid, StackComponent stack, GetVerbsEvent<AlternativeVerb> args)
{ {
if (!args.CanAccess || !args.CanInteract) if (!args.CanAccess || !args.CanInteract)
@@ -175,16 +129,16 @@ namespace Content.Server.Stack
if (amount <= 0) if (amount <= 0)
{ {
_popupSystem.PopupCursor(Loc.GetString("comp-stack-split-too-small"), Filter.Entities(userUid)); PopupSystem.PopupCursor(Loc.GetString("comp-stack-split-too-small"), Filter.Entities(userUid));
return; return;
} }
if (Split(uid, amount, userTransform.Coordinates, stack) is not {} split) if (Split(uid, amount, userTransform.Coordinates, stack) is not {} split)
return; return;
_handsSystem.PickupOrDrop(userUid, split); HandsSystem.PickupOrDrop(userUid, split);
_popupSystem.PopupCursor(Loc.GetString("comp-stack-split"), Filter.Entities(userUid)); PopupSystem.PopupCursor(Loc.GetString("comp-stack-split"), Filter.Entities(userUid));
} }
} }
} }

View File

@@ -104,7 +104,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
} }
/// <summary> /// <summary>
/// Puts an item any hand, preferring the active hand, or puts it on the floor. /// Puts an item into any hand, preferring the active hand, or puts it on the floor.
/// </summary> /// </summary>
public void PickupOrDrop(EntityUid? uid, EntityUid entity, bool checkActionBlocker = true, bool animateUser = false, SharedHandsComponent? handsComp = null, SharedItemComponent? item = null) public void PickupOrDrop(EntityUid? uid, EntityUid entity, bool checkActionBlocker = true, bool animateUser = false, SharedHandsComponent? handsComp = null, SharedItemComponent? item = null)
{ {

View File

@@ -118,7 +118,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
} }
/// <summary> /// <summary>
/// Enumerate over hands, with the active hand being first. /// Enumerate over held items, starting with the item in the currently active hand (if there is one).
/// </summary> /// </summary>
public IEnumerable<EntityUid> EnumerateHeld(EntityUid uid, SharedHandsComponent? handsComp = null) public IEnumerable<EntityUid> EnumerateHeld(EntityUid uid, SharedHandsComponent? handsComp = null)
{ {

View File

@@ -1,15 +1,22 @@
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Localization; using Robust.Shared.Player;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Timing;
namespace Content.Shared.Stacks namespace Content.Shared.Stacks
{ {
[UsedImplicitly] [UsedImplicitly]
public abstract class SharedStackSystem : EntitySystem public abstract class SharedStackSystem : EntitySystem
{ {
[Dependency] protected readonly SharedPopupSystem PopupSystem = default!;
[Dependency] protected readonly SharedHandsSystem HandsSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -18,9 +25,109 @@ namespace Content.Shared.Stacks
SubscribeLocalEvent<SharedStackComponent, ComponentHandleState>(OnStackHandleState); SubscribeLocalEvent<SharedStackComponent, ComponentHandleState>(OnStackHandleState);
SubscribeLocalEvent<SharedStackComponent, ComponentStartup>(OnStackStarted); SubscribeLocalEvent<SharedStackComponent, ComponentStartup>(OnStackStarted);
SubscribeLocalEvent<SharedStackComponent, ExaminedEvent>(OnStackExamined); SubscribeLocalEvent<SharedStackComponent, ExaminedEvent>(OnStackExamined);
SubscribeLocalEvent<SharedStackComponent, InteractUsingEvent>(OnStackInteractUsing);
} }
public void SetCount(EntityUid uid, int amount, SharedStackComponent? component = null) private void OnStackInteractUsing(EntityUid uid, SharedStackComponent stack, InteractUsingEvent args)
{
if (args.Handled)
return;
if (!TryComp(args.Used, out SharedStackComponent? recipientStack))
return;
if (!TryMergeStacks(uid, args.Used, out var transfered, stack, recipientStack))
return;
args.Handled = true;
// interaction is done, the rest is just generating a pop-up
if (!_gameTiming.IsFirstTimePredicted)
return;
var popupPos = args.ClickLocation;
if (!popupPos.IsValid(EntityManager))
{
popupPos = Transform(args.User).Coordinates;
}
switch (transfered)
{
case > 0:
PopupSystem.PopupCoordinates($"+{transfered}", popupPos, Filter.Local());
if (recipientStack.AvailableSpace == 0)
{
PopupSystem.PopupCoordinates(Loc.GetString("comp-stack-becomes-full"),
popupPos.Offset(new Vector2(0, -0.5f)), Filter.Local());
}
break;
case 0 when recipientStack.AvailableSpace == 0:
PopupSystem.PopupCoordinates(Loc.GetString("comp-stack-already-full"), popupPos, Filter.Local());
break;
}
}
private bool TryMergeStacks(
EntityUid donor,
EntityUid recipient,
out int transfered,
SharedStackComponent? donorStack = null,
SharedStackComponent? recipientStack = null)
{
transfered = 0;
if (!Resolve(recipient, ref recipientStack, false) || !Resolve(donor, ref donorStack, false))
return false;
if (!recipientStack.StackTypeId.Equals(donorStack.StackTypeId))
return false;
transfered = Math.Min(donorStack.Count, recipientStack.AvailableSpace);
SetCount(donor, donorStack.Count - transfered, donorStack);
SetCount(recipient, recipientStack.Count + transfered, recipientStack);
return true;
}
/// <summary>
/// If the given item is a stack, this attempts to find a matching stack in the users hand, and merge with that.
/// </summary>
/// <remarks>
/// If the interaction fails to fully merge the stack, or if this is just not a stack, it will instead try
/// to place it in the user's hand normally.
/// </remarks>
public void TryMergeToHands(
EntityUid item,
EntityUid user,
SharedStackComponent? itemStack = null,
SharedHandsComponent? hands = null)
{
if (!Resolve(user, ref hands, false))
return;
if (!Resolve(item, ref itemStack, false))
{
// This isn't even a stack. Just try to pickup as normal.
HandsSystem.PickupOrDrop(user, item, handsComp: hands);
return;
}
// This is shit code until hands get fixed and give an easy way to enumerate over items, starting with the currently active item.
foreach (var held in HandsSystem.EnumerateHeld(user, hands))
{
TryMergeStacks(item, held, out _, donorStack: itemStack);
if (itemStack.Count == 0)
return;
}
HandsSystem.PickupOrDrop(user, item, handsComp: hands);
}
public virtual void SetCount(EntityUid uid, int amount, SharedStackComponent? component = null)
{ {
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return; return;
@@ -44,11 +151,7 @@ namespace Content.Shared.Stacks
} }
component.Count = amount; component.Count = amount;
component.Dirty(); Dirty(component);
// Queue delete stack if count reaches zero.
if(component.Count <= 0)
QueueDel(uid);
// Change appearance data. // Change appearance data.
if (TryComp(uid, out AppearanceComponent? appearance)) if (TryComp(uid, out AppearanceComponent? appearance))