diff --git a/Content.Client/Construction/ConstructionMenu.cs b/Content.Client/Construction/ConstructionMenu.cs index b5772359b1..12e919752c 100644 --- a/Content.Client/Construction/ConstructionMenu.cs +++ b/Content.Client/Construction/ConstructionMenu.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Content.Client.GameObjects.Components.Construction; +using Content.Client.Interfaces.GameObjects; using Content.Shared.Construction; using Content.Shared.GameObjects.Components.Interactable; using Robust.Client.Graphics; @@ -236,11 +237,10 @@ namespace Content.Client.Construction return; } - if (prototype.Type != ConstructionType.Structure) + if (prototype.Type == ConstructionType.Item) { - // In-hand attackby doesn't exist so this is the best alternative. - var loc = Owner.Owner.GetComponent().GridPosition; - Owner.SpawnGhost(prototype, loc, Direction.North); + Owner.TryStartItemConstruction(prototype.ID); + BuildButton.Pressed = false; return; } diff --git a/Content.Client/GameObjects/Components/Construction/ConstructorComponent.cs b/Content.Client/GameObjects/Components/Construction/ConstructorComponent.cs index 5bc3a31be6..3c44298631 100644 --- a/Content.Client/GameObjects/Components/Construction/ConstructorComponent.cs +++ b/Content.Client/GameObjects/Components/Construction/ConstructorComponent.cs @@ -114,6 +114,12 @@ namespace Content.Client.GameObjects.Components.Construction SendNetworkMessage(msg); } + public void TryStartItemConstruction(string prototypeName) + { + var msg = new TryStartItemConstructionMessage(prototypeName); + SendNetworkMessage(msg); + } + public void ClearGhost(int ghostId) { if (Ghosts.TryGetValue(ghostId, out var ghost)) diff --git a/Content.Server/GameObjects/Components/Construction/ConstructorComponent.cs b/Content.Server/GameObjects/Components/Construction/ConstructorComponent.cs index 81ed05c74e..d23d711cfe 100644 --- a/Content.Server/GameObjects/Components/Construction/ConstructorComponent.cs +++ b/Content.Server/GameObjects/Components/Construction/ConstructorComponent.cs @@ -1,10 +1,8 @@ using System; using Content.Server.GameObjects.Components.Stack; -using Content.Server.GameObjects.EntitySystems; using Content.Server.Utility; using Content.Shared.Construction; using Content.Shared.GameObjects.Components.Construction; -using Content.Shared.Interfaces; using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; @@ -13,7 +11,6 @@ using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Network; using Robust.Shared.IoC; -using Robust.Shared.Localization; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Players; @@ -40,6 +37,9 @@ namespace Content.Server.GameObjects.Components.Construction case TryStartStructureConstructionMessage tryStart: TryStartStructureConstruction(tryStart.Location, tryStart.PrototypeName, tryStart.Angle, tryStart.Ack); break; + case TryStartItemConstructionMessage tryStart: + TryStartItemConstruction(tryStart.PrototypeName); + break; } } @@ -102,5 +102,56 @@ namespace Content.Server.GameObjects.Components.Construction var msg = new AckStructureConstructionMessage(ack); SendNetworkMessage(msg); } + + void TryStartItemConstruction(string prototypeName) + { + var prototype = _prototypeManager.Index(prototypeName); + + if (prototype.Stages.Count < 2) + { + throw new InvalidOperationException($"Prototype '{prototypeName}' does not have enough stages."); + } + + var stage0 = prototype.Stages[0]; + if (!(stage0.Forward is ConstructionStepMaterial matStep)) + { + throw new NotImplementedException(); + } + + // Try to find the stack with the material in the user's hand. + var hands = Owner.GetComponent(); + var activeHand = hands.GetActiveHand?.Owner; + if (activeHand == null) + { + return; + } + + if (!activeHand.TryGetComponent(out StackComponent stack) || !ConstructionComponent.MaterialStackValidFor(matStep, stack)) + { + return; + } + + if (!stack.Use(matStep.Amount)) + { + return; + } + + // OK WE'RE GOOD CONSTRUCTION STARTED. + EntitySystem.Get().Play("/Audio/items/deconstruct.ogg", Owner); + if (prototype.Stages.Count == 2) + { + // Exactly 2 stages, so don't make an intermediate frame. + var ent = _serverEntityManager.SpawnEntity(prototype.Result, Owner.Transform.GridPosition); + hands.PutInHandOrDrop(ent.GetComponent()); + } + else + { + //TODO: Make these viable as an item and try putting them in the players hands + var frame = _serverEntityManager.SpawnEntity("structureconstructionframe", Owner.Transform.GridPosition); + var construction = frame.GetComponent(); + construction.Init(prototype); + } + + } } } diff --git a/Content.Shared/GameObjects/Components/Construction/SharedConstructorComponent.cs b/Content.Shared/GameObjects/Components/Construction/SharedConstructorComponent.cs index 4f609f2829..d7c9389fa2 100644 --- a/Content.Shared/GameObjects/Components/Construction/SharedConstructorComponent.cs +++ b/Content.Shared/GameObjects/Components/Construction/SharedConstructorComponent.cs @@ -48,6 +48,26 @@ namespace Content.Shared.GameObjects.Components.Construction } } + /// + /// Sent client -> server to to tell the server that we started building + /// an item-construction. + /// + [Serializable, NetSerializable] + protected class TryStartItemConstructionMessage : ComponentMessage + { + /// + /// The construction prototype to start building. + /// + public readonly string PrototypeName; + + public TryStartItemConstructionMessage(string prototypeName) + { + Directed = true; + PrototypeName = prototypeName; + } + } + + [Serializable, NetSerializable] protected class AckStructureConstructionMessage : ComponentMessage {