diff --git a/Content.Server/Construction/MachineFrameSystem.cs b/Content.Server/Construction/MachineFrameSystem.cs index e831a5c3e2..82d998e50f 100644 --- a/Content.Server/Construction/MachineFrameSystem.cs +++ b/Content.Server/Construction/MachineFrameSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Stacks; using Content.Shared.Tag; using Content.Shared.Popups; using Robust.Shared.Containers; +using Robust.Shared.Utility; namespace Content.Server.Construction; @@ -48,115 +49,189 @@ public sealed class MachineFrameSystem : EntitySystem private void OnInteractUsing(EntityUid uid, MachineFrameComponent component, InteractUsingEvent args) { - if (!component.HasBoard && TryComp(args.Used, out var machineBoard)) + if (args.Handled) + return; + + if (!component.HasBoard) { - if (_container.TryRemoveFromContainer(args.Used)) - { - // Valid board! - component.BoardContainer.Insert(args.Used); - - // Setup requirements and progress... - ResetProgressAndRequirements(component, machineBoard); - - if (TryComp(uid, out ConstructionComponent? construction)) - { - // So prying the components off works correctly. - _construction.ResetEdge(uid, construction); - } - } + if (TryInsertBoard(uid, args.Used, component)) + args.Handled = true; + return; } - else if (component.HasBoard) + + // Machine parts cannot currently satisfy stack/component/tag restrictions. Similarly stacks cannot satisfy + // component/tag restrictions. However, there is no reason this cannot be supported in the future. If this + // changes, then RegenerateProgress() also needs to be updated. + // + // Note that one entity is ALLOWED to satisfy more than one kind of component or tag requirements. This is + // necessary in order to avoid weird entity-ordering shenanigans in RegenerateProgress(). + + // Handle parts + if (TryComp(args.Used, out var machinePart)) { - if (TryComp(args.Used, out var machinePart)) - { - if (!component.Requirements.ContainsKey(machinePart.PartType)) - return; - - if (component.Progress[machinePart.PartType] != component.Requirements[machinePart.PartType] - && _container.TryRemoveFromContainer(args.Used) && component.PartContainer.Insert(args.Used)) - { - component.Progress[machinePart.PartType]++; - args.Handled = true; - } - } - - if (!args.Handled && TryComp(args.Used, out var stack)) - { - var type = stack.StackTypeId; - if (type == null) - return; - if (!component.MaterialRequirements.ContainsKey(type)) - return; - - if (component.MaterialProgress[type] == component.MaterialRequirements[type]) - return; - - var needed = component.MaterialRequirements[type] - component.MaterialProgress[type]; - var count = stack.Count; - - if (count < needed) - { - if (!component.PartContainer.Insert(stack.Owner)) - return; - - component.MaterialProgress[type] += count; - args.Handled = true; - return; - } - - var splitStack = _stack.Split(args.Used, needed, - Comp(uid).Coordinates, stack); - - if (splitStack == null) - return; - - if (!component.PartContainer.Insert(splitStack.Value)) - return; - - component.MaterialProgress[type] += needed; + if (TryInsertPart(uid, args.Used, component, machinePart)) args.Handled = true; - } + return; + } - if (args.Handled) - { - if (IsComplete(component)) { - _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid); - } - return; - } - - foreach (var (compName, info) in component.ComponentRequirements) - { - if (component.ComponentProgress[compName] >= info.Amount) - continue; - - var registration = _factory.GetRegistration(compName); - - if (!HasComp(args.Used, registration.Type)) - continue; - - if (!_container.TryRemoveFromContainer(args.Used) || !component.PartContainer.Insert(args.Used)) - continue; - component.ComponentProgress[compName]++; + // Handle stacks + if (TryComp(args.Used, out var stack)) + { + if (TryInsertStack(uid, args.Used, component, stack)) args.Handled = true; - return; + return; + } + + // Handle component requirements + foreach (var (compName, info) in component.ComponentRequirements) + { + if (component.ComponentProgress[compName] >= info.Amount) + continue; + + var registration = _factory.GetRegistration(compName); + + if (!HasComp(args.Used, registration.Type)) + continue; + + // Insert the entity, if it hasn't already been inserted + if (!args.Handled) + { + if (!_container.TryRemoveFromContainer(args.Used)) + return; + + args.Handled = true; + if (!component.PartContainer.Insert(args.Used)) + return; } - foreach (var (tagName, info) in component.TagRequirements) + component.ComponentProgress[compName]++; + + if (IsComplete(component)) { - if (component.TagProgress[tagName] >= info.Amount) - continue; - - if (!_tag.HasTag(args.Used, tagName)) - continue; - - if (!_container.TryRemoveFromContainer(args.Used) || !component.PartContainer.Insert(args.Used)) - continue; - component.TagProgress[tagName]++; - args.Handled = true; + _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid); return; } } + + // Handle tag requirements + if (!TryComp(args.Used, out var tagComp)) + return; + + foreach (var (tagName, info) in component.TagRequirements) + { + if (component.TagProgress[tagName] >= info.Amount) + continue; + + if (!_tag.HasTag(tagComp, tagName)) + continue; + + // Insert the entity, if it hasn't already been inserted + if (!args.Handled) + { + if (!_container.TryRemoveFromContainer(args.Used)) + return; + + args.Handled = true; + if (!component.PartContainer.Insert(args.Used)) + return; + } + + component.TagProgress[tagName]++; + args.Handled = true; + + if (IsComplete(component)) + { + _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid); + return; + } + } + } + + /// Whether or not the function had any effect. Does not indicate success. + private bool TryInsertBoard(EntityUid uid, EntityUid used, MachineFrameComponent component) + { + if (!TryComp(used, out var machineBoard)) + return false; + + if (!_container.TryRemoveFromContainer(used)) + return false; + + if (!component.BoardContainer.Insert(used)) + return true; + + ResetProgressAndRequirements(component, machineBoard); + + // Reset edge so that prying the components off works correctly. + if (TryComp(uid, out ConstructionComponent? construction)) + _construction.ResetEdge(uid, construction); + + return true; + } + + /// Whether or not the function had any effect. Does not indicate success. + private bool TryInsertPart(EntityUid uid, EntityUid used, MachineFrameComponent component, MachinePartComponent machinePart) + { + DebugTools.Assert(!HasComp(uid)); + if (!component.Requirements.ContainsKey(machinePart.PartType)) + return false; + + if (component.Progress[machinePart.PartType] >= component.Requirements[machinePart.PartType]) + return false; + + if (!_container.TryRemoveFromContainer(used)) + return false; + + if (!component.PartContainer.Insert(used)) + return true; + + component.Progress[machinePart.PartType]++; + if (IsComplete(component)) + _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid); + + return true; + } + + /// Whether or not the function had any effect. Does not indicate success. + private bool TryInsertStack(EntityUid uid, EntityUid used, MachineFrameComponent component, StackComponent stack) + { + var type = stack.StackTypeId; + + if (!component.MaterialRequirements.ContainsKey(type)) + return false; + + var progress = component.MaterialProgress[type]; + var requirement = component.MaterialRequirements[type]; + var needed = requirement - progress; + + if (needed <= 0) + return false; + + var count = stack.Count; + if (count < needed) + { + if (!_container.TryRemoveFromContainer(used)) + return false; + + if (!component.PartContainer.Insert(used)) + return true; + + component.MaterialProgress[type] += count; + return true; + } + + var splitStack = _stack.Split(used, needed, Transform(uid).Coordinates, stack); + + if (splitStack == null) + return false; + + if (!component.PartContainer.Insert(splitStack.Value)) + return true; + + component.MaterialProgress[type] += needed; + if (IsComplete(component)) + _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid); + + return true; } public bool IsComplete(MachineFrameComponent component) @@ -247,10 +322,14 @@ public sealed class MachineFrameSystem : EntitySystem ResetProgressAndRequirements(component, machineBoard); + // If the following code is updated, you need to make sure that it matches the logic in OnInteractUsing() + foreach (var part in component.PartContainer.ContainedEntities) { if (TryComp(part, out var machinePart)) { + DebugTools.Assert(!HasComp(part)); + // Check this is part of the requirements... if (!component.Requirements.ContainsKey(machinePart.PartType)) continue; @@ -259,21 +338,23 @@ public sealed class MachineFrameSystem : EntitySystem component.Progress[machinePart.PartType] = 1; else component.Progress[machinePart.PartType]++; + + continue; } if (TryComp(part, out var stack)) { var type = stack.StackTypeId; - // Check this is part of the requirements... - if (type == null) - continue; + if (!component.MaterialRequirements.ContainsKey(type)) continue; if (!component.MaterialProgress.ContainsKey(type)) - component.MaterialProgress[type] = 1; + component.MaterialProgress[type] = stack.Count; else - component.MaterialProgress[type]++; + component.MaterialProgress[type] += stack.Count; + + continue; } // I have many regrets. @@ -290,10 +371,13 @@ public sealed class MachineFrameSystem : EntitySystem component.ComponentProgress[compName]++; } + if (!TryComp(part, out var tagComp)) + continue; + // I have MANY regrets. - foreach (var (tagName, _) in component.TagRequirements) + foreach (var tagName in component.TagRequirements.Keys) { - if (!_tag.HasTag(part, tagName)) + if (!_tag.HasTag(tagComp, tagName)) continue; if (!component.TagProgress.ContainsKey(tagName)) diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index b9d779ada6..03e0ea7ba3 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -50,15 +50,15 @@ namespace Content.Server.Stack if (!Resolve(uid, ref stack)) return null; + // Try to remove the amount of things we want to split from the original stack... + if (!Use(uid, amount, stack)) + return null; + // Get a prototype ID to spawn the new entity. Null is also valid, although it should rarely be picked... var prototype = _prototypeManager.TryIndex(stack.StackTypeId, out var stackType) ? stackType.Spawn : Prototype(uid)?.ID; - // Try to remove the amount of things we want to split from the original stack... - if (!Use(uid, amount, stack)) - return null; - // Set the output parameter in the event instance to the newly split stack. var entity = Spawn(prototype, spawnPosition); diff --git a/Content.Shared/Stacks/SharedStackSystem.cs b/Content.Shared/Stacks/SharedStackSystem.cs index f41147fa05..b01c486213 100644 --- a/Content.Shared/Stacks/SharedStackSystem.cs +++ b/Content.Shared/Stacks/SharedStackSystem.cs @@ -167,6 +167,7 @@ namespace Content.Shared.Stacks amount = Math.Min(amount, GetMaxCount(component)); amount = Math.Max(amount, 0); + // Server-side override deletes the entity if count == 0 component.Count = amount; Dirty(component); diff --git a/Content.Shared/Stacks/StackComponent.cs b/Content.Shared/Stacks/StackComponent.cs index 1bd2007f23..4e3375bfa4 100644 --- a/Content.Shared/Stacks/StackComponent.cs +++ b/Content.Shared/Stacks/StackComponent.cs @@ -28,6 +28,7 @@ namespace Content.Shared.Stacks /// /// Set to true to not reduce the count when used. + /// Note that still limits the amount that can be used at any one time. /// [DataField("unlimited")] [ViewVariables(VVAccess.ReadOnly)]