From 3ee480a3b1c922be29e8c741243eb60fb7cc6571 Mon Sep 17 00:00:00 2001 From: Acruid Date: Thu, 2 Jul 2020 14:50:57 -0700 Subject: [PATCH] Deconstruction Features (#1242) * ConstructionSystem now detects when a tool is used on an arbitrary entity. * Refactored building logic from ConstructionComponent to ConstructionSystem. * Add OnDeconstruct events so that deconstruction can be blocked if prerequisites are not met. * Multi-step deconstruction works. Windows can be deconstructed using a screwdriver. * In-hand construction and deconstruction works. * Intermediate entities now have a better name assigned to them when created. * Removed a question mark to appease Jenkins. * Instead of running ExposeData on the existing ItemComponent of an intermediateFrame, the system will now replace the existing ItemComponent with a new one, and run ExposeData on the new one. --- .../Construction/ConstructionComponent.cs | 173 +------ .../Components/Interactable/ToolComponent.cs | 8 +- .../Interactable/WelderComponent.cs | 1 + .../PoweredLightComponent.cs | 18 + .../EntitySystems/ConstructionSystem.cs | 426 +++++++++++++++++- .../Prototypes/Construction/structures.yml | 2 + Resources/Prototypes/Construction/weapons.yml | 6 + .../Prototypes/Entities/Items/materials.yml | 8 + 8 files changed, 480 insertions(+), 162 deletions(-) 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