Refactors stacks to be fully ECS. (#4046)
This commit is contained in:
committed by
GitHub
parent
0f8e330a3d
commit
33fa208214
@@ -1,9 +1,6 @@
|
|||||||
#nullable enable
|
|
||||||
|
|
||||||
using Content.Client.UserInterface.Stylesheets;
|
using Content.Client.UserInterface.Stylesheets;
|
||||||
using Content.Client.Utility;
|
using Content.Client.Utility;
|
||||||
using Content.Shared.GameObjects.Components;
|
using Content.Shared.GameObjects.Components;
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
@@ -17,38 +14,17 @@ namespace Content.Client.GameObjects.Components
|
|||||||
[ComponentReference(typeof(SharedStackComponent))]
|
[ComponentReference(typeof(SharedStackComponent))]
|
||||||
public class StackComponent : SharedStackComponent, IItemStatus
|
public class StackComponent : SharedStackComponent, IItemStatus
|
||||||
{
|
{
|
||||||
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[ComponentDependency] private readonly AppearanceComponent? _appearanceComponent = default!;
|
private bool _uiUpdateNeeded;
|
||||||
|
|
||||||
public Control MakeControl() => new StatusControl(this);
|
public Control MakeControl()
|
||||||
|
|
||||||
public override int Count
|
|
||||||
{
|
{
|
||||||
get => base.Count;
|
return new StatusControl(this);
|
||||||
set
|
|
||||||
{
|
|
||||||
var valueChanged = value != Count;
|
|
||||||
base.Count = value;
|
|
||||||
|
|
||||||
if (valueChanged)
|
|
||||||
{
|
|
||||||
_appearanceComponent?.SetData(StackVisuals.Actual, Count);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_uiUpdateNeeded = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Initialize()
|
public void DirtyUI()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
_uiUpdateNeeded = true;
|
||||||
|
|
||||||
if (!Owner.Deleted)
|
|
||||||
{
|
|
||||||
_appearanceComponent?.SetData(StackVisuals.MaxCount, MaxCount);
|
|
||||||
_appearanceComponent?.SetData(StackVisuals.Hide, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class StatusControl : Control
|
private sealed class StatusControl : Control
|
||||||
|
|||||||
25
Content.Client/GameObjects/EntitySystems/StackSystem.cs
Normal file
25
Content.Client/GameObjects/EntitySystems/StackSystem.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Content.Client.GameObjects.Components;
|
||||||
|
using Content.Shared.GameObjects.Components;
|
||||||
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.EntitySystems
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class StackSystem : SharedStackSystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StackComponent, StackCountChangedEvent>(OnStackCountChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStackCountChanged(EntityUid uid, StackComponent component, StackCountChangedEvent args)
|
||||||
|
{
|
||||||
|
// Dirty the UI now that the stack count has changed.
|
||||||
|
component.DirtyUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using Content.Server.GameObjects.Components.Stack;
|
|
||||||
using Content.Shared.GameObjects.Components;
|
|
||||||
using Content.Shared.Stacks;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
|
|
||||||
namespace Content.Server.Construction
|
|
||||||
{
|
|
||||||
public static class StackHelpers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Spawns a stack of a specified type given an amount.
|
|
||||||
/// </summary>
|
|
||||||
public static IEntity SpawnStack(StackPrototype stack, int amount, EntityCoordinates coordinates, IEntityManager? entityManager = null)
|
|
||||||
{
|
|
||||||
entityManager ??= IoCManager.Resolve<IEntityManager>();
|
|
||||||
|
|
||||||
// TODO: Add more.
|
|
||||||
string prototype = stack.Spawn ?? throw new ArgumentOutOfRangeException(nameof(stack),
|
|
||||||
"Stack type doesn't have a prototype specified yet!");
|
|
||||||
|
|
||||||
var ent = entityManager.SpawnEntity(prototype, coordinates);
|
|
||||||
var stackComponent = ent.GetComponent<StackComponent>();
|
|
||||||
|
|
||||||
stackComponent.Count = Math.Min(amount, stackComponent.MaxCount);
|
|
||||||
|
|
||||||
return ent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.GameObjects.Components.Interactable;
|
using Content.Server.GameObjects.Components.Interactable;
|
||||||
using Content.Server.GameObjects.Components.Stack;
|
using Content.Server.GameObjects.Components.Stack;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
using Content.Server.GameObjects.EntitySystems.DoAfter;
|
using Content.Server.GameObjects.EntitySystems.DoAfter;
|
||||||
using Content.Shared.Construction;
|
using Content.Shared.Construction;
|
||||||
using Content.Shared.GameObjects.Components.Interactable;
|
using Content.Shared.GameObjects.Components.Interactable;
|
||||||
@@ -264,11 +265,17 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MaterialConstructionGraphStep materialStep:
|
case MaterialConstructionGraphStep materialStep:
|
||||||
if (materialStep.EntityValid(eventArgs.Using, out var sharedStack)
|
if (materialStep.EntityValid(eventArgs.Using, out var stack)
|
||||||
&& await doAfterSystem.DoAfter(doAfterArgs) == DoAfterStatus.Finished)
|
&& await doAfterSystem.DoAfter(doAfterArgs) == DoAfterStatus.Finished)
|
||||||
{
|
{
|
||||||
var stack = (StackComponent) sharedStack;
|
var splitStack = new StackSplitEvent() {Amount = materialStep.Amount, SpawnPosition = eventArgs.User.Transform.Coordinates};
|
||||||
valid = stack.Split(materialStep.Amount, eventArgs.User.Transform.Coordinates, out entityUsing);
|
Owner.EntityManager.EventBus.RaiseLocalEvent(stack.Owner.Uid, splitStack);
|
||||||
|
|
||||||
|
if (splitStack.Result != null)
|
||||||
|
{
|
||||||
|
entityUsing = splitStack.Result;
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.Construction;
|
using Content.Server.Construction;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
using Content.Server.Interfaces.GameObjects;
|
using Content.Server.Interfaces.GameObjects;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
@@ -87,7 +88,14 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
|
|
||||||
foreach (var (stackType, amount) in machineBoard.MaterialRequirements)
|
foreach (var (stackType, amount) in machineBoard.MaterialRequirements)
|
||||||
{
|
{
|
||||||
var s = StackHelpers.SpawnStack(stackType, amount, Owner.Transform.Coordinates);
|
var stackSpawn = new StackTypeSpawnEvent()
|
||||||
|
{Amount = amount, StackType = stackType, SpawnPosition = Owner.Transform.Coordinates};
|
||||||
|
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, stackSpawn);
|
||||||
|
|
||||||
|
var s = stackSpawn.Result;
|
||||||
|
|
||||||
|
if (s == null)
|
||||||
|
throw new Exception($"Couldn't spawn stack of type {stackType}!");
|
||||||
|
|
||||||
if (!partContainer.Insert(s))
|
if (!partContainer.Insert(s))
|
||||||
throw new Exception($"Couldn't insert machine material of type {stackType} to machine with prototype {Owner.Prototype?.ID ?? "N/A"}");
|
throw new Exception($"Couldn't insert machine material of type {stackType} to machine with prototype {Owner.Prototype?.ID ?? "N/A"}");
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Construction;
|
using Content.Server.Construction;
|
||||||
using Content.Server.GameObjects.Components.Stack;
|
using Content.Server.GameObjects.Components.Stack;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
using Content.Shared.GameObjects.Components.Construction;
|
using Content.Shared.GameObjects.Components.Construction;
|
||||||
using Content.Shared.GameObjects.Components.Tag;
|
using Content.Shared.GameObjects.Components.Tag;
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
using Content.Shared.Interfaces.GameObjects.Components;
|
||||||
@@ -314,10 +315,14 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stack.Split(needed, Owner.Transform.Coordinates, out var newStack))
|
var splitStack = new StackSplitEvent()
|
||||||
|
{Amount = needed, SpawnPosition = Owner.Transform.Coordinates};
|
||||||
|
Owner.EntityManager.EventBus.RaiseLocalEvent(stack.Owner.Uid, splitStack);
|
||||||
|
|
||||||
|
if (splitStack.Result == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!_partContainer.Insert(newStack))
|
if(!_partContainer.Insert(splitStack.Result))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_materialProgress[type] += needed;
|
_materialProgress[type] += needed;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.GameObjects.Components.Stack;
|
using Content.Server.GameObjects.Components.Stack;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
using Content.Shared.Audio;
|
using Content.Shared.Audio;
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
using Content.Shared.Interfaces.GameObjects.Components;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
@@ -78,8 +79,14 @@ namespace Content.Server.GameObjects.Components.Items
|
|||||||
var tile = mapGrid.GetTileRef(location);
|
var tile = mapGrid.GetTileRef(location);
|
||||||
var baseTurf = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];
|
var baseTurf = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];
|
||||||
|
|
||||||
if (HasBaseTurf(currentTileDefinition, baseTurf.Name) && stack.Use(1))
|
if (HasBaseTurf(currentTileDefinition, baseTurf.Name))
|
||||||
{
|
{
|
||||||
|
var stackUse = new StackUseEvent() {Amount = 1};
|
||||||
|
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, stackUse);
|
||||||
|
|
||||||
|
if (!stackUse.Result)
|
||||||
|
continue;
|
||||||
|
|
||||||
PlaceAt(mapGrid, location, currentTileDefinition.TileId);
|
PlaceAt(mapGrid, location, currentTileDefinition.TileId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.GameObjects.Components.Stack;
|
using Content.Server.GameObjects.Components.Stack;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
using Content.Shared.GameObjects.Components.Damage;
|
||||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||||
@@ -41,10 +42,13 @@ namespace Content.Server.GameObjects.Components.Medical
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out StackComponent? stack) &&
|
if (Owner.HasComponent<StackComponent>())
|
||||||
!stack.Use(1))
|
|
||||||
{
|
{
|
||||||
return true;
|
var stackUse = new StackUseEvent() {Amount = 1};
|
||||||
|
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, stackUse);
|
||||||
|
|
||||||
|
if(!stackUse.Result)
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (type, amount) in Heal)
|
foreach (var (type, amount) in Heal)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Robust.Shared.IoC;
|
|||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
@@ -49,8 +50,16 @@ namespace Content.Server.GameObjects.Components.Power
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Owner.TryGetComponent<StackComponent>(out var stack) && !stack.Use(1))
|
|
||||||
return true;
|
if (Owner.HasComponent<StackComponent>())
|
||||||
|
{
|
||||||
|
var stackUse = new StackUseEvent(){Amount = 1};
|
||||||
|
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, stackUse);
|
||||||
|
|
||||||
|
if(!stackUse.Result)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Owner.EntityManager.SpawnEntity(_wirePrototypeID, grid.GridTileToLocal(snapPos));
|
Owner.EntityManager.SpawnEntity(_wirePrototypeID, grid.GridTileToLocal(snapPos));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Content.Shared.GameObjects.Components;
|
using Content.Shared.GameObjects.Components;
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
using Content.Shared.Interfaces;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
@@ -17,114 +10,10 @@ namespace Content.Server.GameObjects.Components.Stack
|
|||||||
// TODO: Naming and presentation and such could use some improvement.
|
// TODO: Naming and presentation and such could use some improvement.
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(SharedStackComponent))]
|
[ComponentReference(typeof(SharedStackComponent))]
|
||||||
public class StackComponent : SharedStackComponent, IInteractUsing, IExamine
|
public class StackComponent : SharedStackComponent, IExamine
|
||||||
{
|
{
|
||||||
private bool _throwIndividually = false;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public bool ThrowIndividually
|
public bool ThrowIndividually { get; set; } = false;
|
||||||
{
|
|
||||||
get => _throwIndividually;
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
_throwIndividually = value;
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(int amount)
|
|
||||||
{
|
|
||||||
Count += amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Try to use an amount of items on this stack.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="amount"></param>
|
|
||||||
/// <returns>True if there were enough items to remove, false if not in which case nothing was changed.</returns>
|
|
||||||
public bool Use(int amount)
|
|
||||||
{
|
|
||||||
if (Count >= amount)
|
|
||||||
{
|
|
||||||
Count -= amount;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attempts to split this stack in two.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="amount">amount the new stack will have</param>
|
|
||||||
/// <param name="spawnPosition">the position the new stack will spawn at</param>
|
|
||||||
/// <param name="stack">the new stack</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool Split(int amount, EntityCoordinates spawnPosition, [NotNullWhen(true)] out IEntity? stack)
|
|
||||||
{
|
|
||||||
if (Count >= amount)
|
|
||||||
{
|
|
||||||
Count -= amount;
|
|
||||||
|
|
||||||
stack = Owner.EntityManager.SpawnEntity(Owner.Prototype?.ID, spawnPosition);
|
|
||||||
|
|
||||||
if (stack.TryGetComponent(out StackComponent? stackComp))
|
|
||||||
{
|
|
||||||
stackComp.Count = amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.Using.TryGetComponent<StackComponent>(out var stack))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!stack.StackTypeId.Equals(StackTypeId))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var toTransfer = Math.Min(Count, stack.AvailableSpace);
|
|
||||||
Count -= toTransfer;
|
|
||||||
stack.Add(toTransfer);
|
|
||||||
|
|
||||||
var popupPos = eventArgs.ClickLocation;
|
|
||||||
if (popupPos == EntityCoordinates.Invalid)
|
|
||||||
{
|
|
||||||
popupPos = eventArgs.User.Transform.Coordinates;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (toTransfer > 0)
|
|
||||||
{
|
|
||||||
popupPos.PopupMessage(eventArgs.User, $"+{toTransfer}");
|
|
||||||
|
|
||||||
if (stack.AvailableSpace == 0)
|
|
||||||
{
|
|
||||||
eventArgs.Using.SpawnTimer(
|
|
||||||
300,
|
|
||||||
() => popupPos.PopupMessage(
|
|
||||||
eventArgs.User,
|
|
||||||
Loc.GetString("comp-stack-becomes-full")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (toTransfer == 0 && stack.AvailableSpace == 0)
|
|
||||||
{
|
|
||||||
popupPos.PopupMessage(
|
|
||||||
eventArgs.User,
|
|
||||||
Loc.GetString("comp-stack-already-full")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
|
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -163,20 +163,22 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
case MaterialConstructionGraphStep materialStep:
|
case MaterialConstructionGraphStep materialStep:
|
||||||
foreach (var entity in EnumerateNearby(user))
|
foreach (var entity in EnumerateNearby(user))
|
||||||
{
|
{
|
||||||
if (!materialStep.EntityValid(entity, out var sharedStack))
|
if (!materialStep.EntityValid(entity, out var stack))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var stack = (StackComponent) sharedStack;
|
var splitStack = new StackSplitEvent()
|
||||||
|
{Amount = materialStep.Amount, SpawnPosition = user.ToCoordinates()};
|
||||||
|
RaiseLocalEvent(entity.Uid, splitStack);
|
||||||
|
|
||||||
if (!stack.Split(materialStep.Amount, user.ToCoordinates(), out var newStack))
|
if (splitStack.Result == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(materialStep.Store))
|
if (string.IsNullOrEmpty(materialStep.Store))
|
||||||
{
|
{
|
||||||
if (!container.Insert(newStack))
|
if (!container.Insert(splitStack.Result))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (!GetContainer(materialStep.Store).Insert(newStack))
|
else if (!GetContainer(materialStep.Store).Insert(splitStack.Result))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
handled = true;
|
handled = true;
|
||||||
@@ -230,7 +232,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
BreakOnStun = true,
|
BreakOnStun = true,
|
||||||
BreakOnTargetMove = false,
|
BreakOnTargetMove = false,
|
||||||
BreakOnUserMove = true,
|
BreakOnUserMove = true,
|
||||||
NeedHand = true,
|
NeedHand = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (await doAfterSystem.DoAfter(doAfterArgs) == DoAfterStatus.Cancelled)
|
if (await doAfterSystem.DoAfter(doAfterArgs) == DoAfterStatus.Cancelled)
|
||||||
|
|||||||
@@ -180,12 +180,13 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
stackComp.Use(1);
|
var splitStack = new StackSplitEvent() {Amount = 1, SpawnPosition = playerEnt.Transform.Coordinates};
|
||||||
throwEnt = throwEnt.EntityManager.SpawnEntity(throwEnt.Prototype?.ID, playerEnt.Transform.Coordinates);
|
RaiseLocalEvent(throwEnt.Uid, splitStack);
|
||||||
|
|
||||||
// can only throw one item at a time, regardless of what the prototype stack size is.
|
if (splitStack.Result == null)
|
||||||
if (throwEnt.TryGetComponent<StackComponent>(out var newStackComp))
|
return false;
|
||||||
newStackComp.Count = 1;
|
|
||||||
|
throwEnt = splitStack.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var direction = coords.ToMapPos(EntityManager) - playerEnt.Transform.WorldPosition;
|
var direction = coords.ToMapPos(EntityManager) - playerEnt.Transform.WorldPosition;
|
||||||
|
|||||||
@@ -65,13 +65,20 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
if (component.Deleted || component.Owner.Deleted)
|
if (component.Deleted || component.Owner.Deleted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
StackComponent? stack = null;
|
var hasStack = component.Owner.HasComponent<StackComponent>();
|
||||||
if (component.RemoveOnInteract && component.Owner.TryGetComponent(out stack) && !stack.Use(1))
|
|
||||||
return;
|
if (hasStack && component.RemoveOnInteract)
|
||||||
|
{
|
||||||
|
var stackUse = new StackUseEvent() {Amount = 1};
|
||||||
|
RaiseLocalEvent(component.Owner.Uid, stackUse);
|
||||||
|
|
||||||
|
if (!stackUse.Result)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid));
|
EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid));
|
||||||
|
|
||||||
if (component.RemoveOnInteract && stack == null && !component.Owner.Deleted)
|
if (component.RemoveOnInteract && !hasStack && !component.Owner.Deleted)
|
||||||
component.Owner.Delete();
|
component.Owner.Delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
256
Content.Server/GameObjects/EntitySystems/StackSystem.cs
Normal file
256
Content.Server/GameObjects/EntitySystems/StackSystem.cs
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Server.GameObjects.Components.Stack;
|
||||||
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
|
using Content.Shared.Interfaces.GameObjects.Components;
|
||||||
|
using Content.Shared.Stacks;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.EntitySystems
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Entity system that handles everything relating to stacks.
|
||||||
|
/// This is a good example for learning how to code in an ECS manner.
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class StackSystem : SharedStackSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StackComponent, InteractUsingEvent>(OnStackInteractUsing);
|
||||||
|
|
||||||
|
// The following subscriptions are basically the "method calls" of this entity system.
|
||||||
|
SubscribeLocalEvent<StackComponent, StackUseEvent>(OnStackUse);
|
||||||
|
SubscribeLocalEvent<StackComponent, StackSplitEvent>(OnStackSplit);
|
||||||
|
SubscribeLocalEvent<StackTypeSpawnEvent>(OnStackTypeSpawn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to use an amount of items on this stack.
|
||||||
|
/// See <see cref="StackUseEvent"/>
|
||||||
|
/// </summary>
|
||||||
|
private void OnStackUse(EntityUid uid, StackComponent stack, StackUseEvent args)
|
||||||
|
{
|
||||||
|
// Check if we have enough things in the stack for this...
|
||||||
|
if (stack.Count < args.Amount)
|
||||||
|
{
|
||||||
|
// Not enough things in the stack, so we set the output result to false.
|
||||||
|
args.Result = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We do have enough things in the stack, so remove them and set the output result to true.
|
||||||
|
RaiseLocalEvent(uid, new StackChangeCountEvent(stack.Count - args.Amount));
|
||||||
|
args.Result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to split this stack into two.
|
||||||
|
/// See <see cref="StackSplitEvent"/>
|
||||||
|
/// </summary>
|
||||||
|
private void OnStackSplit(EntityUid uid, StackComponent stack, StackSplitEvent args)
|
||||||
|
{
|
||||||
|
// If the stack doesn't have enough things as specified in the parameters, we do nothing.
|
||||||
|
if (stack.Count < args.Amount)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get a prototype ID to spawn the new entity. Null is also valid, although it should rarely be picked...
|
||||||
|
var prototype = _prototypeManager.TryIndex<StackPrototype>(stack.StackTypeId, out var stackType)
|
||||||
|
? stackType.Spawn
|
||||||
|
: stack.Owner.Prototype?.ID ?? null;
|
||||||
|
|
||||||
|
// Remove the amount of things we want to split from the original stack...
|
||||||
|
RaiseLocalEvent(uid, new StackChangeCountEvent(stack.Count - args.Amount));
|
||||||
|
|
||||||
|
// Set the output parameter in the event instance to the newly split stack.
|
||||||
|
args.Result = EntityManager.SpawnEntity(prototype, args.SpawnPosition);
|
||||||
|
|
||||||
|
if (args.Result.TryGetComponent(out StackComponent? stackComp))
|
||||||
|
{
|
||||||
|
// Set the split stack's count.
|
||||||
|
RaiseLocalEvent(args.Result.Uid, new StackChangeCountEvent(args.Amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to spawn a stack of a certain type.
|
||||||
|
/// See <see cref="StackTypeSpawnEvent"/>
|
||||||
|
/// </summary>
|
||||||
|
private void OnStackTypeSpawn(StackTypeSpawnEvent args)
|
||||||
|
{
|
||||||
|
// Can't spawn a stack for an invalid type.
|
||||||
|
if (args.StackType == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Set the output result parameter to the new stack entity...
|
||||||
|
args.Result = EntityManager.SpawnEntity(args.StackType.Spawn, args.SpawnPosition);
|
||||||
|
var stack = args.Result.GetComponent<StackComponent>();
|
||||||
|
|
||||||
|
// And finally, set the correct amount!
|
||||||
|
RaiseLocalEvent(args.Result.Uid, new StackChangeCountEvent(args.Amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStackInteractUsing(EntityUid uid, StackComponent stack, InteractUsingEvent args)
|
||||||
|
{
|
||||||
|
if (!args.Used.TryGetComponent<StackComponent>(out var otherStack))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!otherStack.StackTypeId.Equals(stack.StackTypeId))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var toTransfer = Math.Min(stack.Count, otherStack.AvailableSpace);
|
||||||
|
RaiseLocalEvent(uid, new StackChangeCountEvent(stack.Count - toTransfer));
|
||||||
|
RaiseLocalEvent(args.Used.Uid, new StackChangeCountEvent(otherStack.Count + toTransfer));
|
||||||
|
|
||||||
|
var popupPos = args.ClickLocation;
|
||||||
|
if (!popupPos.IsValid(EntityManager))
|
||||||
|
{
|
||||||
|
popupPos = args.User.Transform.Coordinates;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (toTransfer)
|
||||||
|
{
|
||||||
|
case > 0:
|
||||||
|
popupPos.PopupMessage(args.User, $"+{toTransfer}");
|
||||||
|
|
||||||
|
if (otherStack.AvailableSpace == 0)
|
||||||
|
{
|
||||||
|
args.Used.SpawnTimer(
|
||||||
|
300,
|
||||||
|
() => popupPos.PopupMessage(
|
||||||
|
args.User,
|
||||||
|
Loc.GetString("comp-stack-becomes-full")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0 when otherStack.AvailableSpace == 0:
|
||||||
|
popupPos.PopupMessage(
|
||||||
|
args.User,
|
||||||
|
Loc.GetString("comp-stack-already-full")
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following events are actually funny ECS method calls!
|
||||||
|
*
|
||||||
|
* Instead of coupling systems together into a ball of spaghetti,
|
||||||
|
* we raise events that act as method calls.
|
||||||
|
*
|
||||||
|
* So for example, instead of having an Use() method in the
|
||||||
|
* stack component or stack system, we have a StackUseEvent.
|
||||||
|
* Before raising the event, you would set the Amount property,
|
||||||
|
* which acts as a parameter or argument, and afterwards the
|
||||||
|
* entity system in charge of handling this would perform the logic
|
||||||
|
* and then set the Result on the event instance.
|
||||||
|
* Then you can access this property to see whether your Use attempt succeeded.
|
||||||
|
*
|
||||||
|
* This is very powerful, as it completely removes the coupling
|
||||||
|
* between entity systems and allows for greater flexibility.
|
||||||
|
* If you want to intercept this event with another entity system, you can.
|
||||||
|
* And you don't have to write any bad, hacky code for this!
|
||||||
|
* You could even use handled events, or cancellable events...
|
||||||
|
* The possibilities are endless.
|
||||||
|
*
|
||||||
|
* Of course, not everything needs to be directed events!
|
||||||
|
* Broadcast events also work in the same way.
|
||||||
|
* For example, we use a broadcast event to spawn a stack of a certain type.
|
||||||
|
*
|
||||||
|
* Wrapping your head around this may be difficult at first,
|
||||||
|
* but soon you'll get it, coder. Soon you'll grasp the wisdom.
|
||||||
|
* Go forth and write some beautiful and robust code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses an amount of things from a stack.
|
||||||
|
/// Whether this succeeded is stored in <see cref="Result"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class StackUseEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of things to use on the stack.
|
||||||
|
/// Consider this the equivalent of a parameter for a method call.
|
||||||
|
/// </summary>
|
||||||
|
public int Amount { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the action succeeded or not.
|
||||||
|
/// Set by the <see cref="StackSystem"/> after handling this event.
|
||||||
|
/// Consider this the equivalent of a return value for a method call.
|
||||||
|
/// </summary>
|
||||||
|
public bool Result { get; set; } = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to split a stack into two.
|
||||||
|
/// If this succeeds, <see cref="Result"/> will be the new stack.
|
||||||
|
/// </summary>
|
||||||
|
public class StackSplitEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of things to take from the original stack.
|
||||||
|
/// Input parameter.
|
||||||
|
/// </summary>
|
||||||
|
public int Amount { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The position where to spawn the new stack.
|
||||||
|
/// Input parameter.
|
||||||
|
/// </summary>
|
||||||
|
public EntityCoordinates SpawnPosition { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The newly split stack. May be null if the split failed.
|
||||||
|
/// Output parameter.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity? Result { get; set; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to spawn a stack of a certain type.
|
||||||
|
/// If this succeeds, <see cref="Result"/> will be the new stack.
|
||||||
|
/// </summary>
|
||||||
|
public class StackTypeSpawnEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of things the spawned stack will have.
|
||||||
|
/// Input parameter.
|
||||||
|
/// </summary>
|
||||||
|
public int Amount { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The stack type to be spawned.
|
||||||
|
/// Input parameter.
|
||||||
|
/// </summary>
|
||||||
|
public StackPrototype? StackType { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The position where the new stack will be spawned.
|
||||||
|
/// Input parameter.
|
||||||
|
/// </summary>
|
||||||
|
public EntityCoordinates SpawnPosition { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The newly spawned stack, or null if this failed.
|
||||||
|
/// Output parameter.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity? Result { get; set; } = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="GameObjects\Components\Trigger\" />
|
<Folder Include="GameObjects\Components\Trigger\" />
|
||||||
<EmbeddedResource Include="Text\Names\*.txt" />
|
<EmbeddedResource Include="Text\Names\*.txt" />
|
||||||
<Folder Include="GameObjects\Components\Visualizers" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Analyzers.targets" />
|
<Import Project="..\RobustToolbox\MSBuild\Robust.Analyzers.targets" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,46 +1,34 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Log;
|
|
||||||
using Robust.Shared.Players;
|
using Robust.Shared.Players;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Shared.GameObjects.Components
|
namespace Content.Shared.GameObjects.Components
|
||||||
{
|
{
|
||||||
public abstract class SharedStackComponent : Component, ISerializationHooks
|
public abstract class SharedStackComponent : Component, ISerializationHooks
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
private const string SerializationCache = "stack";
|
|
||||||
|
|
||||||
public sealed override string Name => "Stack";
|
public sealed override string Name => "Stack";
|
||||||
public sealed override uint? NetID => ContentNetIDs.STACK;
|
public sealed override uint? NetID => ContentNetIDs.STACK;
|
||||||
|
|
||||||
[DataField("count")]
|
|
||||||
private int _count = 30;
|
|
||||||
|
|
||||||
[DataField("max")]
|
[DataField("max")]
|
||||||
private int _maxCount = 30;
|
private int _maxCount = 30;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public virtual int Count
|
[DataField("stackType", required:true, customTypeSerializer:typeof(PrototypeIdSerializer<StackPrototype>))]
|
||||||
{
|
public string StackTypeId { get; private set; } = string.Empty;
|
||||||
get => _count;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_count = value;
|
|
||||||
if (_count <= 0)
|
|
||||||
{
|
|
||||||
Owner.Delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
Dirty();
|
/// <summary>
|
||||||
}
|
/// Current stack count.
|
||||||
}
|
/// Do NOT set this directly, raise the <see cref="StackChangeCountEvent"/> event instead.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("count")]
|
||||||
|
public int Count { get; set; } = 30;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public int MaxCount
|
public int MaxCount
|
||||||
@@ -48,28 +36,16 @@ namespace Content.Shared.GameObjects.Components
|
|||||||
get => _maxCount;
|
get => _maxCount;
|
||||||
private set
|
private set
|
||||||
{
|
{
|
||||||
|
if (_maxCount == value)
|
||||||
|
return;
|
||||||
|
|
||||||
_maxCount = value;
|
_maxCount = value;
|
||||||
Dirty();
|
Dirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[ViewVariables] public int AvailableSpace => MaxCount - Count;
|
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("stackType")]
|
public int AvailableSpace => MaxCount - Count;
|
||||||
public string StackTypeId { get; } = string.Empty;
|
|
||||||
|
|
||||||
public StackPrototype StackType => _prototypeManager.Index<StackPrototype>(StackTypeId);
|
|
||||||
|
|
||||||
protected override void Startup()
|
|
||||||
{
|
|
||||||
base.Startup();
|
|
||||||
|
|
||||||
if (StackTypeId != string.Empty && !_prototypeManager.HasIndex<StackPrototype>(StackTypeId))
|
|
||||||
{
|
|
||||||
Logger.Error($"No {nameof(StackPrototype)} found with id {StackTypeId} for {Owner.Prototype?.ID ?? Owner.Name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState(ICommonSession player)
|
public override ComponentState GetComponentState(ICommonSession player)
|
||||||
{
|
{
|
||||||
@@ -79,11 +55,10 @@ namespace Content.Shared.GameObjects.Components
|
|||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||||
{
|
{
|
||||||
if (curState is not StackComponentState cast)
|
if (curState is not StackComponentState cast)
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
Count = cast.Count;
|
// This will change the count and call events.
|
||||||
|
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new StackChangeCountEvent(cast.Count));
|
||||||
MaxCount = cast.MaxCount;
|
MaxCount = cast.MaxCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
108
Content.Shared/GameObjects/EntitySystems/SharedStackSystem.cs
Normal file
108
Content.Shared/GameObjects/EntitySystems/SharedStackSystem.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using Content.Shared.GameObjects.Components;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Shared.GameObjects.EntitySystems
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public abstract class SharedStackSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SharedStackComponent, ComponentStartup>(OnStackStarted);
|
||||||
|
SubscribeLocalEvent<SharedStackComponent, StackChangeCountEvent>(OnStackCountChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStackStarted(EntityUid uid, SharedStackComponent component, ComponentStartup args)
|
||||||
|
{
|
||||||
|
if (!ComponentManager.TryGetComponent(uid, out SharedAppearanceComponent? appearance))
|
||||||
|
return;
|
||||||
|
|
||||||
|
appearance.SetData(StackVisuals.MaxCount, component.MaxCount);
|
||||||
|
appearance.SetData(StackVisuals.Hide, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnStackCountChange(EntityUid uid, SharedStackComponent component, StackChangeCountEvent args)
|
||||||
|
{
|
||||||
|
if (args.Amount == component.Count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var old = component.Count;
|
||||||
|
|
||||||
|
if (args.Amount > component.MaxCount)
|
||||||
|
{
|
||||||
|
args.Amount = component.MaxCount;
|
||||||
|
args.Clamped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Amount < 0)
|
||||||
|
{
|
||||||
|
args.Amount = 0;
|
||||||
|
args.Clamped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
component.Count = args.Amount;
|
||||||
|
component.Dirty();
|
||||||
|
|
||||||
|
// Queue delete stack if count reaches zero.
|
||||||
|
if(component.Count <= 0)
|
||||||
|
EntityManager.QueueDeleteEntity(uid);
|
||||||
|
|
||||||
|
// Change appearance data.
|
||||||
|
if (ComponentManager.TryGetComponent(uid, out SharedAppearanceComponent? appearance))
|
||||||
|
appearance.SetData(StackVisuals.Actual, component.Count);
|
||||||
|
|
||||||
|
RaiseLocalEvent(uid, new StackCountChangedEvent(old, component.Count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to change the amount of things in a stack to a specific number.
|
||||||
|
/// If the amount had to be clamped to zero or the max amount, <see cref="Clamped"/> will be true
|
||||||
|
/// and the amount will be changed to match the value set.
|
||||||
|
/// Does nothing if the amount is the same as the stack count already.
|
||||||
|
/// </summary>
|
||||||
|
public class StackChangeCountEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Amount to set the stack to.
|
||||||
|
/// Input/Output parameter.
|
||||||
|
/// </summary>
|
||||||
|
public int Amount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the <see cref="Amount"/> had to be clamped.
|
||||||
|
/// Output parameter.
|
||||||
|
/// </summary>
|
||||||
|
public bool Clamped { get; set; }
|
||||||
|
|
||||||
|
public StackChangeCountEvent(int amount)
|
||||||
|
{
|
||||||
|
Amount = amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event raised when a stack's count has changed.
|
||||||
|
/// </summary>
|
||||||
|
public class StackCountChangedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The old stack count.
|
||||||
|
/// </summary>
|
||||||
|
public int OldCount { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The new stack count.
|
||||||
|
/// </summary>
|
||||||
|
public int NewCount { get; }
|
||||||
|
|
||||||
|
public StackCountChangedEvent(int oldCount, int newCount)
|
||||||
|
{
|
||||||
|
OldCount = oldCount;
|
||||||
|
NewCount = newCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,12 +17,12 @@ namespace Content.Shared.Stacks
|
|||||||
public string Name { get; } = string.Empty;
|
public string Name { get; } = string.Empty;
|
||||||
|
|
||||||
[DataField("icon")]
|
[DataField("icon")]
|
||||||
public SpriteSpecifier? Icon { get; }
|
public SpriteSpecifier? Icon { get; } = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entity id that will be spawned by default from this stack.
|
/// The entity id that will be spawned by default from this stack.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("spawn")]
|
[DataField("spawn", required: true)]
|
||||||
public string? Spawn { get; }
|
public string Spawn { get; } = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user