diff --git a/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs b/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs index e6e7ea0d33..bf01fb63a3 100644 --- a/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs +++ b/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs @@ -84,32 +84,6 @@ namespace Content.IntegrationTests.Tests.Construction Assert.That(valid, Is.True, $"One or more SpawnPrototype actions specified invalid entity prototypes!\n{message}"); } - [Test] - public async Task ConstructionGraphNodeEntityPrototypeValid() - { - await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true }); - var server = pairTracker.Pair.Server; - - var protoMan = server.ResolveDependency(); - - var valid = true; - var message = new StringBuilder(); - - foreach (var graph in protoMan.EnumeratePrototypes()) - { - foreach (var node in graph.Nodes.Values) - { - if (string.IsNullOrEmpty(node.Entity) || protoMan.TryIndex(node.Entity, out EntityPrototype _)) continue; - - valid = false; - message.Append($"Invalid entity prototype \"{node.Entity}\" on node \"{node.Name}\" of graph \"{graph.ID}\"\n"); - } - } - await pairTracker.CleanReturnAsync(); - - Assert.That(valid, Is.True, $"One or more nodes specified invalid entity prototypes!\n{message}"); - } - [Test] public async Task ConstructionGraphEdgeValid() { diff --git a/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs b/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs index 6d68bfd4f3..7e196526bb 100644 --- a/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs @@ -124,6 +124,7 @@ namespace Content.IntegrationTests.Tests.Construction var server = pairTracker.Pair.Server; var protoMan = server.ResolveDependency(); + var entMan = server.ResolveDependency(); foreach (var proto in protoMan.EnumeratePrototypes()) { @@ -135,8 +136,9 @@ namespace Content.IntegrationTests.Tests.Construction Assert.That(graph.TryPath(start, target, out var path), $"Unable to find path from \"{start}\" to \"{target}\" on graph \"{graph.ID}\""); Assert.That(path, Has.Length.GreaterThanOrEqualTo(1), $"Unable to find path from \"{start}\" to \"{target}\" on graph \"{graph.ID}\"."); var next = path[0]; - Assert.That(next.Entity, Is.Not.Null, $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) must specify an entity! Graph: {graph.ID}"); - Assert.That(protoMan.TryIndex(next.Entity, out EntityPrototype entity), $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) specified an invalid entity prototype ({next.Entity})"); + var nextId = next.Entity.GetId(null, null, new(entMan)); + Assert.That(nextId, Is.Not.Null, $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) must specify an entity! Graph: {graph.ID}"); + Assert.That(protoMan.TryIndex(nextId, out EntityPrototype entity), $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) specified an invalid entity prototype ({nextId} [{next.Entity}])"); Assert.That(entity.Components.ContainsKey("Construction"), $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) specified an entity prototype ({next.Entity}) without a ConstructionComponent."); #pragma warning restore NUnit2045 } diff --git a/Content.Server/Construction/Completions/BuildComputer.cs b/Content.Server/Construction/Completions/BuildComputer.cs deleted file mode 100644 index c4ec7a1aad..0000000000 --- a/Content.Server/Construction/Completions/BuildComputer.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Linq; -using Content.Server.Construction.Components; -using Content.Shared.Construction; -using JetBrains.Annotations; -using Robust.Server.Containers; -using Robust.Shared.Containers; - -namespace Content.Server.Construction.Completions -{ - [UsedImplicitly] - [DataDefinition] - public sealed class BuildComputer : IGraphAction - { - [DataField("container")] public string Container { get; private set; } = string.Empty; - - // TODO use or generalize ConstructionSystem.ChangeEntity(); - // TODO pass in node/edge & graph ID for better error logs. - public void PerformAction(EntityUid uid, EntityUid? userUid, IEntityManager entityManager) - { - if (!entityManager.TryGetComponent(uid, out ContainerManagerComponent? containerManager)) - { - Logger.Error($"Computer entity {entityManager.ToPrettyString(uid)} did not have a container manager! Aborting build computer action."); - return; - } - - var containerSystem = entityManager.EntitySysManager.GetEntitySystem(); - - if (!containerSystem.TryGetContainer(uid, Container, out var container, containerManager)) - { - Logger.Error($"Computer entity {entityManager.ToPrettyString(uid)} did not have the specified '{Container}' container! Aborting build computer action."); - return; - } - - if (container.ContainedEntities.Count != 1) - { - Logger.Error($"Computer entity {entityManager.ToPrettyString(uid)} did not have exactly one item in the specified '{Container}' container! Aborting build computer action."); - return; - } - - var board = container.ContainedEntities[0]; - - if (!entityManager.TryGetComponent(board, out ComputerBoardComponent? boardComponent)) - { - Logger.Error($"Computer entity {entityManager.ToPrettyString(uid)} had an invalid entity in container \"{Container}\"! Aborting build computer action."); - return; - } - - container.Remove(board); - - var transform = entityManager.GetComponent(uid); - var computer = entityManager.SpawnEntity(boardComponent.Prototype, transform.Coordinates); - entityManager.GetComponent(computer).LocalRotation = transform.LocalRotation; - - var computerContainer = containerSystem.EnsureContainer(computer, Container); - - // In case it already existed and there are any entities inside the container, delete them. - foreach (var ent in computerContainer.ContainedEntities.ToArray()) - { - computerContainer.ForceRemove(ent); - entityManager.DeleteEntity(ent); - } - - computerContainer.Insert(board); - - // We only add this container. If some construction needs to take other containers into account, fix this. - entityManager.EntitySysManager.GetEntitySystem().AddContainer(computer, Container); - - var entChangeEv = new ConstructionChangeEntityEvent(computer, uid); - entityManager.EventBus.RaiseLocalEvent(uid, entChangeEv); - entityManager.EventBus.RaiseLocalEvent(computer, entChangeEv, broadcast: true); - - // Delete the original entity. - entityManager.QueueDeleteEntity(uid); - } - } -} diff --git a/Content.Server/Construction/Completions/BuildMachine.cs b/Content.Server/Construction/Completions/BuildMachine.cs deleted file mode 100644 index 909fd5ab1b..0000000000 --- a/Content.Server/Construction/Completions/BuildMachine.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.Linq; -using Content.Server.Construction.Components; -using Content.Shared.Construction; -using Content.Shared.Construction.Components; -using JetBrains.Annotations; -using Robust.Server.Containers; -using Robust.Shared.Containers; - -namespace Content.Server.Construction.Completions -{ - [UsedImplicitly] - [DataDefinition] - public sealed class BuildMachine : IGraphAction - { - // TODO use or generalize ConstructionSystem.ChangeEntity(); - public void PerformAction(EntityUid uid, EntityUid? userUid, IEntityManager entityManager) - { - if (!entityManager.TryGetComponent(uid, out ContainerManagerComponent? containerManager)) - { - Logger.Warning($"Machine frame entity {uid} did not have a container manager! Aborting build machine action."); - return; - } - - if (!entityManager.TryGetComponent(uid, out MachineFrameComponent? machineFrame)) - { - Logger.Warning($"Machine frame entity {uid} did not have a machine frame component! Aborting build machine action."); - return; - } - - if (!entityManager.EntitySysManager.GetEntitySystem().IsComplete(machineFrame)) - { - Logger.Warning($"Machine frame entity {uid} doesn't have all required parts to be built! Aborting build machine action."); - return; - } - - if (!containerManager.TryGetContainer(MachineFrameComponent.BoardContainerName, out var entBoardContainer)) - { - Logger.Warning($"Machine frame entity {uid} did not have the '{MachineFrameComponent.BoardContainerName}' container! Aborting build machine action."); - return; - } - - if (!containerManager.TryGetContainer(MachineFrameComponent.PartContainerName, out var entPartContainer)) - { - Logger.Warning($"Machine frame entity {uid} did not have the '{MachineFrameComponent.PartContainerName}' container! Aborting build machine action."); - return; - } - - if (entBoardContainer.ContainedEntities.Count != 1) - { - Logger.Warning($"Machine frame entity {uid} did not have exactly one item in the '{MachineFrameComponent.BoardContainerName}' container! Aborting build machine action."); - } - - var board = entBoardContainer.ContainedEntities[0]; - - if (!entityManager.TryGetComponent(board, out MachineBoardComponent? boardComponent)) - { - Logger.Warning($"Machine frame entity {uid} had an invalid entity in container \"{MachineFrameComponent.BoardContainerName}\"! Aborting build machine action."); - return; - } - - entBoardContainer.Remove(board); - - var containerSys = entityManager.EntitySysManager.GetEntitySystem(); - var transform = entityManager.GetComponent(uid); - var machine = entityManager.SpawnEntity(boardComponent.Prototype, transform.Coordinates); - entityManager.GetComponent(machine).LocalRotation = transform.LocalRotation; - - var boardContainer = containerSys.EnsureContainer(machine, MachineFrameComponent.BoardContainerName, out var existed); - - if (existed) - { - // Clean that up... - containerSys.CleanContainer(boardContainer); - } - - var partContainer = containerSys.EnsureContainer(machine, MachineFrameComponent.PartContainerName, out existed); - - if (existed) - { - // Clean that up, too... - containerSys.CleanContainer(partContainer); - } - - boardContainer.Insert(board); - - // Now we insert all parts. - foreach (var part in entPartContainer.ContainedEntities.ToArray()) - { - entPartContainer.ForceRemove(part); - partContainer.Insert(part); - } - - var constructionSystem = entityManager.EntitySysManager.GetEntitySystem(); - if (entityManager.TryGetComponent(machine, out ConstructionComponent? construction)) - { - // We only add these two container. If some construction needs to take other containers into account, fix this. - constructionSystem.AddContainer(machine, MachineFrameComponent.BoardContainerName, construction); - constructionSystem.AddContainer(machine, MachineFrameComponent.PartContainerName, construction); - } - - if (entityManager.TryGetComponent(machine, out MachineComponent? machineComp)) - { - constructionSystem.RefreshParts(machine, machineComp); - } - - var entChangeEv = new ConstructionChangeEntityEvent(machine, uid); - entityManager.EventBus.RaiseLocalEvent(uid, entChangeEv); - entityManager.EventBus.RaiseLocalEvent(machine, entChangeEv, broadcast: true); - entityManager.QueueDeleteEntity(uid); - } - } -} diff --git a/Content.Server/Construction/ConstructionSystem.Graph.cs b/Content.Server/Construction/ConstructionSystem.Graph.cs index 8a848495c8..49dd9dd359 100644 --- a/Content.Server/Construction/ConstructionSystem.Graph.cs +++ b/Content.Server/Construction/ConstructionSystem.Graph.cs @@ -256,7 +256,8 @@ namespace Content.Server.Construction $"{ToPrettyString(userUid.Value):player} changed {ToPrettyString(uid):entity}'s node from \"{oldNode}\" to \"{id}\""); // ChangeEntity will handle the pathfinding update. - if (node.Entity is {} newEntity && ChangeEntity(uid, userUid, newEntity, construction) != null) + if (node.Entity.GetId(uid, userUid, new(EntityManager)) is {} newEntity + && ChangeEntity(uid, userUid, newEntity, construction) != null) return true; if(performActions) @@ -323,8 +324,6 @@ namespace Content.Server.Construction } } - EntityManager.InitializeAndStartEntity(newUid); - // We set the graph and node accordingly. ChangeGraph(newUid, userUid, construction.Graph, construction.Node, false, newConstruction); @@ -374,6 +373,13 @@ namespace Content.Server.Construction RaiseLocalEvent(uid, entChangeEv); RaiseLocalEvent(newUid, entChangeEv, broadcast: true); + foreach (var logic in GetCurrentNode(newUid, newConstruction)!.TransformLogic) + { + logic.Transform(uid, newUid, userUid, new(EntityManager)); + } + + EntityManager.InitializeAndStartEntity(newUid); + QueueDel(uid); return newUid; diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index 9c50b3d9a4..564e1cd24e 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -252,7 +252,7 @@ namespace Content.Server.Construction return null; } - var newEntityProto = graph.Nodes[edge.Target].Entity; + var newEntityProto = graph.Nodes[edge.Target].Entity.GetId(null, user, new(EntityManager)); var newEntity = EntityManager.SpawnEntity(newEntityProto, EntityManager.GetComponent(user).Coordinates); if (!TryComp(newEntity, out ConstructionComponent? construction)) diff --git a/Content.Server/Construction/MachineFrameSystem.cs b/Content.Server/Construction/MachineFrameSystem.cs index fc2dfd6363..fe40baf610 100644 --- a/Content.Server/Construction/MachineFrameSystem.cs +++ b/Content.Server/Construction/MachineFrameSystem.cs @@ -37,7 +37,7 @@ public sealed class MachineFrameSystem : EntitySystem { RegenerateProgress(component); - if (TryComp(uid, out var construction)) + if (TryComp(uid, out var construction) && construction.TargetNode == null) { // Attempt to set pathfinding to the machine node... _construction.SetPathfindingTarget(uid, "machine", construction); diff --git a/Content.Server/Construction/NodeEntities/BoardNodeEntity.cs b/Content.Server/Construction/NodeEntities/BoardNodeEntity.cs new file mode 100644 index 0000000000..237d13be96 --- /dev/null +++ b/Content.Server/Construction/NodeEntities/BoardNodeEntity.cs @@ -0,0 +1,41 @@ +using Content.Server.Construction.Components; +using Content.Shared.Construction; +using Content.Shared.Construction.Components; +using JetBrains.Annotations; +using Robust.Server.Containers; + +namespace Content.Server.Construction.NodeEntities; + +/// +/// Works for both and +/// because duplicating code just for this is really stinky. +/// +[UsedImplicitly] +[DataDefinition] +public sealed class BoardNodeEntity : IGraphNodeEntity +{ + [DataField("container")] public string Container { get; private set; } = string.Empty; + + public string? GetId(EntityUid? uid, EntityUid? userUid, GraphNodeEntityArgs args) + { + if (uid == null) + return null; + + var containerSystem = args.EntityManager.EntitySysManager.GetEntitySystem(); + + if (!containerSystem.TryGetContainer(uid.Value, Container, out var container) + || container.ContainedEntities.Count == 0) + return null; + + var board = container.ContainedEntities[0]; + + // There should not be a case where both of these components exist on the same entity... + if (args.EntityManager.TryGetComponent(board, out MachineBoardComponent? machine)) + return machine.Prototype; + + if(args.EntityManager.TryGetComponent(board, out ComputerBoardComponent? computer)) + return computer.Prototype; + + return null; + } +} diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index 423aed19a7..0a3ee47a65 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -25,6 +25,5 @@ - diff --git a/Content.Shared/Construction/ConstructionGraphNode.cs b/Content.Shared/Construction/ConstructionGraphNode.cs index eab54f8a22..1519587e92 100644 --- a/Content.Shared/Construction/ConstructionGraphNode.cs +++ b/Content.Shared/Construction/ConstructionGraphNode.cs @@ -1,4 +1,6 @@ using System.Diagnostics.CodeAnalysis; +using Content.Shared.Construction.NodeEntities; +using Content.Shared.Construction.Serialization; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -23,8 +25,11 @@ namespace Content.Shared.Construction [ViewVariables] public IReadOnlyList Actions => _actions; - [DataField("entity", customTypeSerializer:typeof(PrototypeIdSerializer))] - public string? Entity { get; private set; } + [DataField("transform")] + public IGraphTransform[] TransformLogic = Array.Empty(); + + [DataField("entity", customTypeSerializer: typeof(GraphNodeEntitySerializer), serverOnly:true)] + public IGraphNodeEntity Entity { get; private set; } = new NullNodeEntity(); public ConstructionGraphEdge? GetEdge(string target) { diff --git a/Content.Shared/Construction/IGraphNodeEntity.cs b/Content.Shared/Construction/IGraphNodeEntity.cs new file mode 100644 index 0000000000..facd0e9a88 --- /dev/null +++ b/Content.Shared/Construction/IGraphNodeEntity.cs @@ -0,0 +1,29 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Construction; + +public interface IGraphNodeEntity +{ + /// + /// Gets the ID for a node, given the of both the + /// construction entity and the user entity. + /// If the construction entity is null, then we are dealing with a "start construction" for an entity that + /// does not exist yet. + /// If the user entity is null, this node was reached through means other some sort of "user interaction". + /// + /// Uid of the construction entity. + /// Uid of the user that caused the transition to the node. + /// Arguments with useful instances, etc. + /// + public string? GetId(EntityUid? uid, EntityUid? userUid, GraphNodeEntityArgs args); +} + +public readonly struct GraphNodeEntityArgs +{ + public readonly IEntityManager EntityManager; + + public GraphNodeEntityArgs(IEntityManager entityManager) + { + EntityManager = entityManager; + } +} diff --git a/Content.Shared/Construction/IGraphTransform.cs b/Content.Shared/Construction/IGraphTransform.cs new file mode 100644 index 0000000000..f61ff87706 --- /dev/null +++ b/Content.Shared/Construction/IGraphTransform.cs @@ -0,0 +1,16 @@ +namespace Content.Shared.Construction; + +public interface IGraphTransform +{ + public void Transform(EntityUid oldUid, EntityUid newUid, EntityUid? userUid, GraphTransformArgs args); +} + +public readonly struct GraphTransformArgs +{ + public readonly IEntityManager EntityManager; + + public GraphTransformArgs(IEntityManager entityManager) + { + EntityManager = entityManager; + } +} diff --git a/Content.Shared/Construction/NodeEntities/NullNodeEntity.cs b/Content.Shared/Construction/NodeEntities/NullNodeEntity.cs new file mode 100644 index 0000000000..3ca89931ba --- /dev/null +++ b/Content.Shared/Construction/NodeEntities/NullNodeEntity.cs @@ -0,0 +1,13 @@ +using JetBrains.Annotations; + +namespace Content.Shared.Construction.NodeEntities; + +[UsedImplicitly] +[DataDefinition] +public sealed class NullNodeEntity : IGraphNodeEntity +{ + public string? GetId(EntityUid? uid, EntityUid? userUid, GraphNodeEntityArgs args) + { + return null; + } +} diff --git a/Content.Shared/Construction/NodeEntities/StaticNodeEntity.cs b/Content.Shared/Construction/NodeEntities/StaticNodeEntity.cs new file mode 100644 index 0000000000..c5bc734575 --- /dev/null +++ b/Content.Shared/Construction/NodeEntities/StaticNodeEntity.cs @@ -0,0 +1,28 @@ +using JetBrains.Annotations; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Construction.NodeEntities; + +[UsedImplicitly] +[DataDefinition] +public sealed class StaticNodeEntity : IGraphNodeEntity +{ + [DataField("id", customTypeSerializer:typeof(PrototypeIdSerializer))] + [ViewVariables(VVAccess.ReadWrite)] + public string? Id { get; } + + public StaticNodeEntity() + { + } + + public StaticNodeEntity(string id) + { + Id = id; + } + + public string? GetId(EntityUid? uid, EntityUid? userUid, GraphNodeEntityArgs args) + { + return Id; + } +} diff --git a/Content.Shared/Construction/Serialization/GraphNodeEntitySerializer.cs b/Content.Shared/Construction/Serialization/GraphNodeEntitySerializer.cs new file mode 100644 index 0000000000..b15ef2b7ee --- /dev/null +++ b/Content.Shared/Construction/Serialization/GraphNodeEntitySerializer.cs @@ -0,0 +1,53 @@ +using Content.Shared.Construction.NodeEntities; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Markdown; +using Robust.Shared.Serialization.Markdown.Mapping; +using Robust.Shared.Serialization.Markdown.Validation; +using Robust.Shared.Serialization.Markdown.Value; +using Robust.Shared.Serialization.TypeSerializers.Interfaces; + +namespace Content.Shared.Construction.Serialization; + +public sealed class GraphNodeEntitySerializer : ITypeSerializer, ITypeSerializer +{ + public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node, + IDependencyCollection dependencies, ISerializationContext? context = null) + { + var id = node.Value; + + var protoMan = dependencies.Resolve(); + + if (!protoMan.HasIndex(id)) + { + return new ErrorNode(node, $"Entity Prototype {id} was not found!"); + } + + return new ValidatedValueNode(node); + } + + public IGraphNodeEntity Read(ISerializationManager serializationManager, ValueDataNode node, + IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null, ISerializationManager.InstantiationDelegate? instanceProvider = null) + { + return new StaticNodeEntity(node.Value); + } + + public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node, + IDependencyCollection dependencies, ISerializationContext? context = null) + { + return serializationManager.ValidateNode(node, context); + } + + public IGraphNodeEntity Read(ISerializationManager serializationManager, MappingDataNode node, + IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null, ISerializationManager.InstantiationDelegate? instanceProvider = null) + { + return serializationManager.Read(node, hookCtx, context, instanceProvider, false); + } + + public DataNode Write(ISerializationManager serializationManager, IGraphNodeEntity value, IDependencyCollection dependencies, + bool alwaysWrite = false, ISerializationContext? context = null) + { + return serializationManager.WriteValue(value, alwaysWrite, context, false); + } +} diff --git a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs index 0688d35400..a5e151f4ea 100644 --- a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs @@ -32,11 +32,14 @@ namespace Content.Shared.Storage.EntitySystems val.Layer = layerName; } - if (EntityManager.TryGetComponent(component.Owner, out AppearanceComponent? appearanceComponent)) + if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent)) { var list = new List(component.MapLayers.Keys); - _appearance.SetData(component.Owner, StorageMapVisuals.InitLayers, new ShowLayerData(list), appearanceComponent); + _appearance.SetData(uid, StorageMapVisuals.InitLayers, new ShowLayerData(list), appearanceComponent); } + + // Ensure appearance is correct with current contained entities. + UpdateAppearance(uid, component); } private void MapperEntityRemoved(EntityUid uid, ItemMapperComponent itemMapper, @@ -45,7 +48,7 @@ namespace Content.Shared.Storage.EntitySystems if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID)) return; - UpdateAppearance(uid, itemMapper, args); + UpdateAppearance(uid, itemMapper); } private void MapperEntityInserted(EntityUid uid, ItemMapperComponent itemMapper, @@ -54,40 +57,43 @@ namespace Content.Shared.Storage.EntitySystems if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID)) return; - UpdateAppearance(uid, itemMapper, args); + UpdateAppearance(uid, itemMapper); } - private void UpdateAppearance(EntityUid uid, ItemMapperComponent itemMapper, ContainerModifiedMessage message) + private void UpdateAppearance(EntityUid uid, ItemMapperComponent? itemMapper = null) { - if (EntityManager.TryGetComponent(itemMapper.Owner, out AppearanceComponent? appearanceComponent) - && TryGetLayers(message, itemMapper, out var containedLayers)) + if(!Resolve(uid, ref itemMapper)) + return; + + if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent) + && TryGetLayers(uid, itemMapper, out var containedLayers)) { - _appearance.SetData(itemMapper.Owner, StorageMapVisuals.LayerChanged, new ShowLayerData(containedLayers), appearanceComponent); + _appearance.SetData(uid, StorageMapVisuals.LayerChanged, new ShowLayerData(containedLayers), appearanceComponent); } } /// - /// Method that iterates over storage of the entity in and sets according to + /// Method that iterates over storage of the entity in and sets according to /// definition. It will have O(n*m) time behavior (n - number of entities in container, and m - number of /// definitions in . /// - /// event with EntityUid used to search the storage + /// EntityUid used to search the storage /// component that contains definition used to map whitelist in /// mapLayers to string. /// /// list of layers that should be visible /// false if msg.Container.Owner is not a storage, true otherwise. - private bool TryGetLayers(ContainerModifiedMessage msg, + private bool TryGetLayers(EntityUid uid, ItemMapperComponent itemMapper, out List showLayers) { - var containedLayers = _container.GetAllContainers(msg.Container.Owner) + var containedLayers = _container.GetAllContainers(uid) .Where(c => itemMapper.ContainerWhitelist?.Contains(c.ID) ?? true).SelectMany(cont => cont.ContainedEntities).ToArray(); var list = new List(); foreach (var mapLayerData in itemMapper.MapLayers.Values) { - var count = containedLayers.Count(uid => mapLayerData.ServerWhitelist.IsValid(uid)); + var count = containedLayers.Count(ent => mapLayerData.ServerWhitelist.IsValid(ent)); if (count >= mapLayerData.MinCount && count <= mapLayerData.MaxCount) { list.Add(mapLayerData.Layer); diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml index 32316a5a41..d2c87deaa6 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml @@ -13,6 +13,8 @@ - type: Construction graph: Computer node: computer + containers: + - board - type: Computer - type: ApcPowerReceiver powerLoad: 200 diff --git a/Resources/Prototypes/Entities/Structures/Machines/frame.yml b/Resources/Prototypes/Entities/Structures/Machines/frame.yml index 19be323117..67ad742639 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/frame.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/frame.yml @@ -28,6 +28,9 @@ - type: Rotatable - type: Pullable - type: Construction + containers: + - machine_board + - machine_parts graph: Machine node: missingWires - type: ContainerContainer @@ -83,6 +86,9 @@ graph: Machine node: machineFrame defaultTarget: machine + containers: + - machine_board + - machine_parts - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/machines/computer.yml b/Resources/Prototypes/Recipes/Construction/Graphs/machines/computer.yml index 9a05f3daf0..c0736c4973 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/machines/computer.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/machines/computer.yml @@ -107,15 +107,12 @@ - to: computer conditions: - !type:EntityAnchored {} - completed: - - !type:BuildComputer - container: board steps: - tool: Screwing - to: monitorMissing conditions: - - !type:EntityAnchored { } + - !type:EntityAnchored {} completed: - !type:SpawnPrototype prototype: SheetGlass1 @@ -124,6 +121,7 @@ - tool: Prying - node: computer + entity: !type:BoardNodeEntity { container: board } edges: - to: monitorUnsecured steps: diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/machines/machine.yml b/Resources/Prototypes/Recipes/Construction/Graphs/machines/machine.yml index 29c9b6a35c..cfbcaf6767 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/machines/machine.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/machines/machine.yml @@ -48,6 +48,10 @@ - node: machineFrame entity: MachineFrame actions: + - !type:AddContainer + container: machine_parts + - !type:AddContainer + container: machine_board - !type:MachineFrameRegenerateProgress edges: - to: machine @@ -60,9 +64,6 @@ guideIconParts: sprite: Objects/Misc/stock_parts.rsi state: scan_module - completed: - # Yes, this is snowflaked so we don't have to make an unique graph per machine. You're welcome. - - !type:BuildMachine steps: - tool: Screwing doAfter: 0.5 @@ -93,11 +94,7 @@ doAfter: 0.25 - node: machine - actions: - - !type:AddContainer - container: machine_parts - - !type:AddContainer - container: machine_board + entity: !type:BoardNodeEntity { container: machine_board } edges: - to: machineFrame completed: