diff --git a/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs b/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs
index f55f32cd75..1eb9cad50e 100644
--- a/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs
+++ b/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs
@@ -1,168 +1,41 @@
-using System;
-using System.Collections.Generic;
-using Content.Server.GameObjects.Components.Interactable;
-using Content.Server.GameObjects.Components.Stack;
-using Content.Server.GameObjects.EntitySystems;
-using Content.Server.Interfaces;
-using Content.Server.Utility;
-using Content.Shared.Construction;
-using Content.Shared.GameObjects.Components;
-using Content.Shared.GameObjects.Components.Interactable;
-using Robust.Server.GameObjects;
-using Robust.Server.GameObjects.EntitySystems;
-using Robust.Server.Interfaces.GameObjects;
+using Content.Shared.Construction;
using Robust.Shared.GameObjects;
-using Robust.Shared.GameObjects.Systems;
-using Robust.Shared.Interfaces.GameObjects;
-using Robust.Shared.Interfaces.GameObjects.Components;
-using Robust.Shared.Interfaces.Random;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
+using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
-using static Content.Shared.Construction.ConstructionStepMaterial;
namespace Content.Server.GameObjects.Components.Construction
{
+ ///
+ /// Holds data about an entity that is in the process of being constructed or destructed.
+ ///
[RegisterComponent]
- public class ConstructionComponent : Component, IInteractUsing
+ public class ConstructionComponent : Component
{
+ ///
public override string Name => "Construction";
+ ///
+ /// The current construction recipe being used to build this entity.
+ ///
[ViewVariables]
- public ConstructionPrototype Prototype { get; private set; }
+ public ConstructionPrototype Prototype { get; set; }
+
+ ///
+ /// The current stage of construction.
+ ///
[ViewVariables]
- public int Stage { get; private set; }
+ public int Stage { get; set; }
- SpriteComponent Sprite;
- ITransformComponent Transform;
-
- public override void Initialize()
+ ///
+ public override void ExposeData(ObjectSerializer serializer)
{
- base.Initialize();
+ base.ExposeData(serializer);
- Sprite = Owner.GetComponent();
- Transform = Owner.GetComponent();
- }
+ serializer.DataReadWriteFunction("prototype", null,
+ value => Prototype = value, () => Prototype);
- public bool InteractUsing(InteractUsingEventArgs eventArgs)
- {
- // default interaction check for AttackBy allows inside blockers, so we will check if its blocked if
- // we're not allowed to build on impassable stuff
- if (Prototype.CanBuildInImpassable == false)
- {
- if (!InteractionChecks.InRangeUnobstructed(eventArgs, false))
- return false;
- }
-
- var stage = Prototype.Stages[Stage];
-
- if (TryProcessStep(stage.Forward, eventArgs.Using, eventArgs.User))
- {
- Stage++;
- if (Stage == Prototype.Stages.Count - 1)
- {
- // Oh boy we get to finish construction!
- var entMgr = IoCManager.Resolve();
- var ent = entMgr.SpawnEntity(Prototype.Result, Transform.GridPosition);
- ent.GetComponent().LocalRotation = Transform.LocalRotation;
- Owner.Delete();
- return true;
- }
-
- stage = Prototype.Stages[Stage];
- if (stage.Icon != null)
- {
- Sprite.LayerSetSprite(0, stage.Icon);
- }
- }
-
- else if (TryProcessStep(stage.Backward, eventArgs.Using, eventArgs.User))
- {
- Stage--;
- if (Stage == 0)
- {
- // Deconstruction complete.
- Owner.Delete();
- return true;
- }
-
- stage = Prototype.Stages[Stage];
- if (stage.Icon != null)
- {
- Sprite.LayerSetSprite(0, stage.Icon);
- }
- }
-
- return true;
- }
-
- public void Init(ConstructionPrototype prototype)
- {
- Prototype = prototype;
- Stage = 1;
- if(prototype.Stages[1].Icon != null)
- {
- Sprite.AddLayerWithSprite(prototype.Stages[1].Icon);
- }
- else
- {
- Sprite.AddLayerWithSprite(prototype.Icon);
- }
-
-
- }
-
- bool TryProcessStep(ConstructionStep step, IEntity slapped, IEntity user)
- {
- if (step == null)
- {
- return false;
- }
- var sound = EntitySystem.Get();
-
- switch (step)
- {
- case ConstructionStepMaterial matStep:
- if (!slapped.TryGetComponent(out StackComponent stack)
- || !MaterialStackValidFor(matStep, stack)
- || !stack.Use(matStep.Amount))
- {
- return false;
- }
- if (matStep.Material == MaterialType.Cable)
- sound.PlayAtCoords("/Audio/items/zip.ogg", Transform.GridPosition);
- else
- sound.PlayAtCoords("/Audio/items/deconstruct.ogg", Transform.GridPosition);
- return true;
- case ConstructionStepTool toolStep:
- if (!slapped.TryGetComponent(out var tool))
- return false;
-
- // Handle welder manually since tool steps specify fuel amount needed, for some reason.
- if (toolStep.ToolQuality.HasFlag(ToolQuality.Welding))
- return slapped.TryGetComponent(out var welder)
- && welder.UseTool(user, Owner, toolStep.ToolQuality, toolStep.Amount);
-
- return tool.UseTool(user, Owner, toolStep.ToolQuality);
-
- default:
- throw new NotImplementedException();
- }
- }
-
- private static Dictionary StackTypeMap
- = new Dictionary
- {
- { StackType.Cable, MaterialType.Cable },
- { StackType.Gold, MaterialType.Gold },
- { StackType.Glass, MaterialType.Glass },
- { StackType.Metal, MaterialType.Metal }
- };
-
- // Really this should check the actual materials at play..
- public static bool MaterialStackValidFor(ConstructionStepMaterial step, StackComponent stack)
- {
- return StackTypeMap.TryGetValue((StackType)stack.StackType, out var should) && should == step.Material;
+ serializer.DataReadWriteFunction("stage", 0,
+ value => Stage = value, () => Stage);
}
}
}
diff --git a/Content.Server/GameObjects/Components/Interactable/ToolComponent.cs b/Content.Server/GameObjects/Components/Interactable/ToolComponent.cs
index d2adaaa212..bfa1ac94ee 100644
--- a/Content.Server/GameObjects/Components/Interactable/ToolComponent.cs
+++ b/Content.Server/GameObjects/Components/Interactable/ToolComponent.cs
@@ -15,8 +15,14 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Interactable
{
+ public interface IToolComponent
+ {
+ ToolQuality Qualities { get; set; }
+ }
+
[RegisterComponent]
- public class ToolComponent : SharedToolComponent
+ [ComponentReference(typeof(IToolComponent))]
+ public class ToolComponent : SharedToolComponent, IToolComponent
{
protected ToolQuality _qualities = ToolQuality.None;
diff --git a/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs b/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs
index 4b3b049b4f..03d2b85479 100644
--- a/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs
+++ b/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs
@@ -22,6 +22,7 @@ namespace Content.Server.GameObjects.Components.Interactable
{
[RegisterComponent]
[ComponentReference(typeof(ToolComponent))]
+ [ComponentReference(typeof(IToolComponent))]
public class WelderComponent : ToolComponent, IExamine, IUse, ISuicideAct
{
#pragma warning disable 649
diff --git a/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs b/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs
index 4847424727..4cc6ecebd9 100644
--- a/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs
+++ b/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs
@@ -2,6 +2,7 @@
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
+using Content.Server.Interfaces;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Robust.Server.GameObjects;
@@ -48,6 +49,23 @@ namespace Content.Server.GameObjects.Components.Power
}
}
+ public override void HandleMessage(ComponentMessage message, IComponent component)
+ {
+ base.HandleMessage(message, component);
+
+ switch (message)
+ {
+ case BeginDeconstructCompMsg msg:
+ if (!msg.BlockDeconstruct && !(_lightBulbContainer.ContainedEntity is null))
+ {
+ var notifyManager = IoCManager.Resolve();
+ notifyManager.PopupMessage(Owner, msg.User, "Remove the bulb.");
+ msg.BlockDeconstruct = true;
+ }
+ break;
+ }
+ }
+
public bool InteractUsing(InteractUsingEventArgs eventArgs)
{
return InsertBulb(eventArgs.Using);
diff --git a/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs b/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs
index 5d4a169a3a..7e656d7cd1 100644
--- a/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs
+++ b/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs
@@ -1,20 +1,27 @@
using System;
+using System.Collections.Generic;
using Content.Server.GameObjects.Components.Construction;
+using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Stack;
+using Content.Server.Interfaces;
using Content.Server.Utility;
using Content.Shared.Construction;
+using Content.Shared.GameObjects.Components;
+using Content.Shared.GameObjects.Components.Interactable;
using JetBrains.Annotations;
+using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
-using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
+using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.EntitySystems
{
@@ -27,16 +34,25 @@ namespace Content.Server.GameObjects.EntitySystems
#pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] private readonly IMapManager _mapManager;
- [Dependency] private readonly IServerEntityManager _serverEntityManager;
+ [Dependency] private readonly IServerNotifyManager _notifyManager;
#pragma warning restore 649
+ private readonly Dictionary _craftRecipes = new Dictionary();
+
///
public override void Initialize()
{
base.Initialize();
+ foreach (var prototype in _prototypeManager.EnumeratePrototypes())
+ {
+ _craftRecipes.Add(prototype.Result, prototype);
+ }
+
SubscribeNetworkEvent(HandleStartStructureConstruction);
SubscribeNetworkEvent(HandleStartItemConstruction);
+
+ SubscribeLocalEvent(HandleToolInteraction);
}
private void HandleStartStructureConstruction(TryStartStructureConstructionMessage msg, EntitySessionEventArgs args)
@@ -55,6 +71,168 @@ namespace Content.Server.GameObjects.EntitySystems
TryStartItemConstruction(placingEnt, msg.PrototypeName);
}
+ private void HandleToolInteraction(AfterAttackMessage msg)
+ {
+ if(msg.Handled)
+ return;
+
+ var targetEnt = msg.Attacked;
+ var handEnt = msg.ItemInHand;
+
+ // A tool has to interact with an entity.
+ if(targetEnt is null || handEnt is null)
+ return;
+
+ // Cannot deconstruct an entity with no prototype.
+ var targetPrototype = targetEnt.MetaData.EntityPrototype;
+ if (targetPrototype is null)
+ return;
+
+ // the target entity is in the process of being constructed/deconstructed
+ if (msg.Attacked.TryGetComponent(out var constructComp))
+ {
+ var result = TryConstructEntity(constructComp, handEnt, msg.User);
+
+ // TryConstructEntity may delete the existing entity
+
+ msg.Handled = result;
+ }
+ else // try to start the deconstruction process
+ {
+ // A tool was not used on the entity.
+ if (!handEnt.TryGetComponent(out var toolComp))
+ return;
+
+ // no known recipe for entity
+ if (!_craftRecipes.TryGetValue(targetPrototype.ID, out var prototype))
+ {
+ _notifyManager.PopupMessage(msg.Attacked, msg.User,
+ "Cannot be deconstructed.");
+ return;
+ }
+
+ // there is a recipe, but it can't be deconstructed.
+ var lastStep = prototype.Stages[^1].Backward;
+ if (!(lastStep is ConstructionStepTool))
+ {
+ _notifyManager.PopupMessage(msg.Attacked, msg.User,
+ "Cannot be deconstructed.");
+ return;
+ }
+
+ // wrong tool
+ var caps = ((ConstructionStepTool) lastStep).ToolQuality;
+ if ((toolComp.Qualities & caps) == 0)
+ {
+ _notifyManager.PopupMessage(msg.Attacked, msg.User,
+ "Wrong tool to start deconstruct.");
+ return;
+ }
+
+ // ask around and see if the deconstruction prerequisites are satisfied
+ // (remove bulbs, approved access, open panels, etc)
+ var deconCompMsg = new BeginDeconstructCompMsg(msg.User);
+ targetEnt.SendMessage(null, deconCompMsg);
+ if(deconCompMsg.BlockDeconstruct)
+ return;
+
+ var deconEntMsg = new BeginDeconstructEntityMsg(msg.User, handEnt, targetEnt);
+ RaiseLocalEvent(deconEntMsg);
+ if(deconEntMsg.BlockDeconstruct)
+ return;
+
+ // --- GOOD TO GO ---
+
+ // pop off the material and switch to frame
+ var targetEntPos = targetEnt.Transform.MapPosition;
+ if (prototype.Stages.Count <= 2) // there are no intermediate stages
+ {
+ targetEnt.Delete();
+
+ SpawnIngredient(targetEntPos, prototype.Stages[(prototype.Stages.Count - 2)].Forward as ConstructionStepMaterial);
+ }
+ else // replace ent with intermediate
+ {
+ // Spawn frame
+ var frame = EntityManager.SpawnEntity("structureconstructionframe", targetEntPos);
+ var construction = frame.GetComponent();
+ SetupComponent(construction, prototype);
+ construction.Stage = prototype.Stages.Count - 2;
+ SetupDeconIntermediateSprite(construction, prototype);
+ frame.Transform.LocalRotation = targetEnt.Transform.LocalRotation;
+
+ if (targetEnt.Prototype.Components.TryGetValue("Item", out var itemProtoComp))
+ {
+ if(frame.HasComponent())
+ frame.RemoveComponent();
+
+ var itemComp = frame.AddComponent();
+
+ var serializer = YamlObjectSerializer.NewReader(itemProtoComp);
+ itemComp.ExposeData(serializer);
+ }
+
+ ReplaceInContainerOrGround(targetEnt, frame);
+
+ // remove target
+ targetEnt.Delete();
+
+ // spawn material
+ SpawnIngredient(targetEntPos, prototype.Stages[(prototype.Stages.Count-2)].Forward as ConstructionStepMaterial);
+ }
+ }
+ }
+
+ private static void SetupDeconIntermediateSprite(ConstructionComponent constructionComponent, ConstructionPrototype prototype)
+ {
+ if(!constructionComponent.Owner.TryGetComponent(out var spriteComp))
+ return;
+
+ for (var i = prototype.Stages.Count - 1; i < 0; i--)
+ {
+ if (prototype.Stages[i].Icon != null)
+ {
+ spriteComp.AddLayerWithSprite(prototype.Stages[1].Icon);
+ return;
+ }
+ }
+
+ spriteComp.AddLayerWithSprite(prototype.Icon);
+ }
+
+ private void SpawnIngredient(MapCoordinates position, ConstructionStepMaterial lastStep)
+ {
+ if(lastStep is null)
+ return;
+
+ var material = lastStep.Material;
+ var quantity = lastStep.Amount;
+
+ var matEnt = EntityManager.SpawnEntity(MaterialPrototypes[material], position);
+ if (matEnt.TryGetComponent(out var stackComp))
+ {
+ stackComp.Count = quantity;
+ }
+ else
+ {
+ quantity--; // already spawned one above
+ while (quantity > 0)
+ {
+ EntityManager.SpawnEntity(MaterialPrototypes[material], position);
+ quantity--;
+ }
+ }
+ }
+
+ private static readonly Dictionary MaterialPrototypes =
+ new Dictionary
+ {
+ { ConstructionStepMaterial.MaterialType.Cable, "CableStack1" },
+ { ConstructionStepMaterial.MaterialType.Gold, "GoldStack1" },
+ { ConstructionStepMaterial.MaterialType.Metal, "SteelSheet1" },
+ { ConstructionStepMaterial.MaterialType.Glass, "GlassSheet1" }
+ };
+
private bool TryStartStructureConstruction(IEntity placingEnt, GridCoordinates loc, string prototypeName, Angle angle)
{
var prototype = _prototypeManager.Index(prototypeName);
@@ -84,7 +262,7 @@ namespace Content.Server.GameObjects.EntitySystems
return false;
}
- if (!activeHand.TryGetComponent(out StackComponent stack) || !ConstructionComponent.MaterialStackValidFor(matStep, stack))
+ if (!activeHand.TryGetComponent(out StackComponent stack) || !MaterialStackValidFor(matStep, stack))
{
return false;
}
@@ -99,14 +277,14 @@ namespace Content.Server.GameObjects.EntitySystems
if (prototype.Stages.Count == 2)
{
// Exactly 2 stages, so don't make an intermediate frame.
- var ent = _serverEntityManager.SpawnEntity(prototype.Result, loc);
+ var ent = EntityManager.SpawnEntity(prototype.Result, loc);
ent.Transform.LocalRotation = angle;
}
else
{
- var frame = _serverEntityManager.SpawnEntity("structureconstructionframe", loc);
+ var frame = EntityManager.SpawnEntity("structureconstructionframe", loc);
var construction = frame.GetComponent();
- construction.Init(prototype);
+ SetupComponent(construction, prototype);
frame.Transform.LocalRotation = angle;
}
@@ -136,7 +314,7 @@ namespace Content.Server.GameObjects.EntitySystems
return;
}
- if (!activeHand.TryGetComponent(out StackComponent stack) || !ConstructionComponent.MaterialStackValidFor(matStep, stack))
+ if (!activeHand.TryGetComponent(out StackComponent stack) || !MaterialStackValidFor(matStep, stack))
{
return;
}
@@ -151,17 +329,243 @@ namespace Content.Server.GameObjects.EntitySystems
if (prototype.Stages.Count == 2)
{
// Exactly 2 stages, so don't make an intermediate frame.
- var ent = _serverEntityManager.SpawnEntity(prototype.Result, placingEnt.Transform.GridPosition);
+ var ent = EntityManager.SpawnEntity(prototype.Result, placingEnt.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", placingEnt.Transform.GridPosition);
+ var frame = EntityManager.SpawnEntity("structureconstructionframe", placingEnt.Transform.GridPosition);
var construction = frame.GetComponent();
- construction.Init(prototype);
+ SetupComponent(construction, prototype);
+
+ var finalPrototype = _prototypeManager.Index(prototype.Result);
+ if (finalPrototype.Components.TryGetValue("Item", out var itemProtoComp))
+ {
+ if(frame.HasComponent())
+ frame.RemoveComponent();
+
+ var itemComp = frame.AddComponent();
+
+ var serializer = YamlObjectSerializer.NewReader(itemProtoComp);
+ itemComp.ExposeData(serializer);
+
+ hands.PutInHandOrDrop(itemComp);
+ }
+ }
+ }
+
+ private bool TryConstructEntity(ConstructionComponent constructionComponent, IEntity handTool, IEntity user)
+ {
+ var constructEntity = constructionComponent.Owner;
+ var spriteComponent = constructEntity.GetComponent();
+ var transformComponent = constructEntity.GetComponent();
+
+ // default interaction check for AttackBy allows inside blockers, so we will check if its blocked if
+ // we're not allowed to build on impassable stuff
+ var constructPrototype = constructionComponent.Prototype;
+ if (constructPrototype.CanBuildInImpassable == false)
+ {
+ if (!InteractionChecks.InRangeUnobstructed(user, constructEntity.Transform.MapPosition))
+ return false;
}
+ var stage = constructPrototype.Stages[constructionComponent.Stage];
+
+ if (TryProcessStep(constructEntity, stage.Forward, handTool, user, transformComponent.GridPosition))
+ {
+ constructionComponent.Stage++;
+ if (constructionComponent.Stage == constructPrototype.Stages.Count - 1)
+ {
+ // Oh boy we get to finish construction!
+ var ent = EntityManager.SpawnEntity(constructPrototype.Result, transformComponent.GridPosition);
+ ent.Transform.LocalRotation = transformComponent.LocalRotation;
+
+ ReplaceInContainerOrGround(constructEntity, ent);
+
+ constructEntity.Delete();
+ return true;
+ }
+
+ stage = constructPrototype.Stages[constructionComponent.Stage];
+ if (stage.Icon != null)
+ {
+ spriteComponent.LayerSetSprite(0, stage.Icon);
+ }
+ }
+
+ else if (TryProcessStep(constructEntity, stage.Backward, handTool, user, transformComponent.GridPosition))
+ {
+ constructionComponent.Stage--;
+ stage = constructPrototype.Stages[constructionComponent.Stage];
+
+ // If forward needed a material, drop it
+ SpawnIngredient(constructEntity.Transform.MapPosition, stage.Forward as ConstructionStepMaterial);
+
+ if (constructionComponent.Stage == 0)
+ {
+ // Deconstruction complete.
+ constructEntity.Delete();
+ return true;
+ }
+
+ if (stage.Icon != null)
+ {
+ spriteComponent.LayerSetSprite(0, stage.Icon);
+ }
+ }
+
+ return true;
+ }
+
+ private static void ReplaceInContainerOrGround(IEntity oldEntity, IEntity newEntity)
+ {
+ var parentEntity = oldEntity.Transform.Parent?.Owner;
+ if (!(parentEntity is null) && parentEntity.TryGetComponent(out var containerMan))
+ {
+ if (containerMan.TryGetContainer(oldEntity, out var container))
+ {
+ container.ForceRemove(oldEntity);
+ container.Insert(newEntity);
+ }
+ }
+ }
+
+ private bool TryProcessStep(IEntity constructEntity, ConstructionStep step, IEntity slapped, IEntity user, GridCoordinates gridCoords)
+ {
+ if (step == null)
+ {
+ return false;
+ }
+
+ var sound = EntitySystemManager.GetEntitySystem();
+
+ switch (step)
+ {
+ case ConstructionStepMaterial matStep:
+ if (!slapped.TryGetComponent(out StackComponent stack)
+ || !MaterialStackValidFor(matStep, stack)
+ || !stack.Use(matStep.Amount))
+ {
+ return false;
+ }
+ if (matStep.Material == ConstructionStepMaterial.MaterialType.Cable)
+ sound.PlayAtCoords("/Audio/items/zip.ogg", gridCoords);
+ else
+ sound.PlayAtCoords("/Audio/items/deconstruct.ogg", gridCoords);
+ return true;
+ case ConstructionStepTool toolStep:
+ if (!slapped.TryGetComponent(out var tool))
+ return false;
+
+ // Handle welder manually since tool steps specify fuel amount needed, for some reason.
+ if (toolStep.ToolQuality.HasFlag(ToolQuality.Welding))
+ return slapped.TryGetComponent(out var welder)
+ && welder.UseTool(user, constructEntity, toolStep.ToolQuality, toolStep.Amount);
+
+ return tool.UseTool(user, constructEntity, toolStep.ToolQuality);
+
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ // Really this should check the actual materials at play..
+ private static readonly Dictionary StackTypeMap
+ = new Dictionary
+ {
+ { StackType.Cable, ConstructionStepMaterial.MaterialType.Cable },
+ { StackType.Gold, ConstructionStepMaterial.MaterialType.Gold },
+ { StackType.Glass, ConstructionStepMaterial.MaterialType.Glass },
+ { StackType.Metal, ConstructionStepMaterial.MaterialType.Metal }
+ };
+
+ private static bool MaterialStackValidFor(ConstructionStepMaterial step, StackComponent stack)
+ {
+ return StackTypeMap.TryGetValue((StackType)stack.StackType, out var should) && should == step.Material;
+ }
+
+ private void SetupComponent(ConstructionComponent constructionComponent, ConstructionPrototype prototype)
+ {
+ constructionComponent.Prototype = prototype;
+ constructionComponent.Stage = 1;
+ var spriteComp = constructionComponent.Owner.GetComponent();
+ if(prototype.Stages[1].Icon != null)
+ {
+ spriteComp.AddLayerWithSprite(prototype.Stages[1].Icon);
+ }
+ else
+ {
+ spriteComp.AddLayerWithSprite(prototype.Icon);
+ }
+
+ var frame = constructionComponent.Owner;
+ var finalPrototype = _prototypeManager.Index(prototype.Result);
+
+ frame.Name = $"Unfinished {finalPrototype.Name}";
+ }
+ }
+
+ ///
+ /// A system message that is raised when an entity is trying to be deconstructed.
+ ///
+ public class BeginDeconstructEntityMsg : EntitySystemMessage
+ {
+ ///
+ /// Entity that initiated the deconstruction.
+ ///
+ public IEntity User { get; }
+
+ ///
+ /// Tool in the active hand of the user.
+ ///
+ public IEntity Hand { get; }
+
+ ///
+ /// Target entity that is trying to be deconstructed.
+ ///
+ public IEntity Target { get; }
+
+ ///
+ /// Set this to true if you would like to block the deconstruction from happening.
+ ///
+ public bool BlockDeconstruct { get; set; }
+
+ ///
+ /// Constructs a new instance of .
+ ///
+ /// Entity that initiated the deconstruction.
+ /// Tool in the active hand of the user.
+ /// Target entity that is trying to be deconstructed.
+ public BeginDeconstructEntityMsg(IEntity user, IEntity hand, IEntity target)
+ {
+ User = user;
+ Hand = hand;
+ Target = target;
+ }
+ }
+
+ ///
+ /// A component message that is raised when an entity is trying to be deconstructed.
+ ///
+ public class BeginDeconstructCompMsg : ComponentMessage
+ {
+ ///
+ /// Entity that initiated the deconstruction.
+ ///
+ public IEntity User { get; }
+
+ ///
+ /// Set this to true if you would like to block the deconstruction from happening.
+ ///
+ public bool BlockDeconstruct { get; set; }
+
+ ///
+ /// Constructs a new instance of .
+ ///
+ /// Entity that initiated the deconstruction.
+ public BeginDeconstructCompMsg(IEntity user)
+ {
+ User = user;
}
}
}
diff --git a/Resources/Prototypes/Construction/structures.yml b/Resources/Prototypes/Construction/structures.yml
index ec4ab72967..b529c544ec 100644
--- a/Resources/Prototypes/Construction/structures.yml
+++ b/Resources/Prototypes/Construction/structures.yml
@@ -48,6 +48,8 @@
steps:
- material: Glass
amount: 2
+ reverse:
+ tool: Screwing
- type: construction
name: low wall
diff --git a/Resources/Prototypes/Construction/weapons.yml b/Resources/Prototypes/Construction/weapons.yml
index 5633572957..e5d9c78a5a 100644
--- a/Resources/Prototypes/Construction/weapons.yml
+++ b/Resources/Prototypes/Construction/weapons.yml
@@ -10,10 +10,16 @@
steps:
- material: Metal
amount: 1
+ reverse:
+ tool: Screwing
- material: Cable
amount: 3
+ reverse:
+ tool: Screwing
# Should probably use a shard but y'know.
- material: Glass
amount: 1
+ reverse:
+ tool: Screwing
diff --git a/Resources/Prototypes/Entities/Items/materials.yml b/Resources/Prototypes/Entities/Items/materials.yml
index 9f3e8f51a7..7a30fe9ee9 100644
--- a/Resources/Prototypes/Entities/Items/materials.yml
+++ b/Resources/Prototypes/Entities/Items/materials.yml
@@ -82,6 +82,14 @@
bounds:
all: -0.15,-0.15,0.15,0.15
+- type: entity
+ id: CableStack1
+ name: cable stack 1
+ parent: CableStack
+ components:
+ - type: Stack
+ count: 1
+
- type: entity
id: HVWireStack
name: HV Wire Coil