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.
This commit is contained in:
Acruid
2020-07-02 14:50:57 -07:00
committed by GitHub
parent 6c205c4a79
commit 3ee480a3b1
8 changed files with 480 additions and 162 deletions

View File

@@ -1,168 +1,41 @@
using System; using Content.Shared.Construction;
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 Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.Serialization;
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.ViewVariables; using Robust.Shared.ViewVariables;
using static Content.Shared.Construction.ConstructionStepMaterial;
namespace Content.Server.GameObjects.Components.Construction namespace Content.Server.GameObjects.Components.Construction
{ {
/// <summary>
/// Holds data about an entity that is in the process of being constructed or destructed.
/// </summary>
[RegisterComponent] [RegisterComponent]
public class ConstructionComponent : Component, IInteractUsing public class ConstructionComponent : Component
{ {
/// <inheritdoc />
public override string Name => "Construction"; public override string Name => "Construction";
/// <summary>
/// The current construction recipe being used to build this entity.
/// </summary>
[ViewVariables] [ViewVariables]
public ConstructionPrototype Prototype { get; private set; } public ConstructionPrototype Prototype { get; set; }
/// <summary>
/// The current stage of construction.
/// </summary>
[ViewVariables] [ViewVariables]
public int Stage { get; private set; } public int Stage { get; set; }
SpriteComponent Sprite; /// <inheritdoc />
ITransformComponent Transform; public override void ExposeData(ObjectSerializer serializer)
public override void Initialize()
{ {
base.Initialize(); base.ExposeData(serializer);
Sprite = Owner.GetComponent<SpriteComponent>(); serializer.DataReadWriteFunction("prototype", null,
Transform = Owner.GetComponent<ITransformComponent>(); value => Prototype = value, () => Prototype);
}
public bool InteractUsing(InteractUsingEventArgs eventArgs) serializer.DataReadWriteFunction("stage", 0,
{ value => Stage = value, () => Stage);
// 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<IServerEntityManager>();
var ent = entMgr.SpawnEntity(Prototype.Result, Transform.GridPosition);
ent.GetComponent<ITransformComponent>().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<AudioSystem>();
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<ToolComponent>(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<WelderComponent>(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<StackType, MaterialType> StackTypeMap
= new Dictionary<StackType, MaterialType>
{
{ 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;
} }
} }
} }

View File

@@ -15,8 +15,14 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Interactable namespace Content.Server.GameObjects.Components.Interactable
{ {
public interface IToolComponent
{
ToolQuality Qualities { get; set; }
}
[RegisterComponent] [RegisterComponent]
public class ToolComponent : SharedToolComponent [ComponentReference(typeof(IToolComponent))]
public class ToolComponent : SharedToolComponent, IToolComponent
{ {
protected ToolQuality _qualities = ToolQuality.None; protected ToolQuality _qualities = ToolQuality.None;

View File

@@ -22,6 +22,7 @@ namespace Content.Server.GameObjects.Components.Interactable
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(ToolComponent))] [ComponentReference(typeof(ToolComponent))]
[ComponentReference(typeof(IToolComponent))]
public class WelderComponent : ToolComponent, IExamine, IUse, ISuicideAct public class WelderComponent : ToolComponent, IExamine, IUse, ISuicideAct
{ {
#pragma warning disable 649 #pragma warning disable 649

View File

@@ -2,6 +2,7 @@
using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.Components.Sound; using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Robust.Server.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<IServerNotifyManager>();
notifyManager.PopupMessage(Owner, msg.User, "Remove the bulb.");
msg.BlockDeconstruct = true;
}
break;
}
}
public bool InteractUsing(InteractUsingEventArgs eventArgs) public bool InteractUsing(InteractUsingEventArgs eventArgs)
{ {
return InsertBulb(eventArgs.Using); return InsertBulb(eventArgs.Using);

View File

@@ -1,20 +1,27 @@
using System; using System;
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Construction; using Content.Server.GameObjects.Components.Construction;
using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.Components.Stack;
using Content.Server.Interfaces;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.Construction; using Content.Shared.Construction;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Interactable;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.GameObjects.EntitySystems
{ {
@@ -27,16 +34,25 @@ namespace Content.Server.GameObjects.EntitySystems
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager; [Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] private readonly IMapManager _mapManager; [Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly IServerEntityManager _serverEntityManager; [Dependency] private readonly IServerNotifyManager _notifyManager;
#pragma warning restore 649 #pragma warning restore 649
private readonly Dictionary<string, ConstructionPrototype> _craftRecipes = new Dictionary<string, ConstructionPrototype>();
/// <inheritdoc /> /// <inheritdoc />
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
foreach (var prototype in _prototypeManager.EnumeratePrototypes<ConstructionPrototype>())
{
_craftRecipes.Add(prototype.Result, prototype);
}
SubscribeNetworkEvent<TryStartStructureConstructionMessage>(HandleStartStructureConstruction); SubscribeNetworkEvent<TryStartStructureConstructionMessage>(HandleStartStructureConstruction);
SubscribeNetworkEvent<TryStartItemConstructionMessage>(HandleStartItemConstruction); SubscribeNetworkEvent<TryStartItemConstructionMessage>(HandleStartItemConstruction);
SubscribeLocalEvent<AfterAttackMessage>(HandleToolInteraction);
} }
private void HandleStartStructureConstruction(TryStartStructureConstructionMessage msg, EntitySessionEventArgs args) private void HandleStartStructureConstruction(TryStartStructureConstructionMessage msg, EntitySessionEventArgs args)
@@ -55,6 +71,168 @@ namespace Content.Server.GameObjects.EntitySystems
TryStartItemConstruction(placingEnt, msg.PrototypeName); 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<ConstructionComponent>(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<IToolComponent>(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<ConstructionComponent>();
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<ItemComponent>())
frame.RemoveComponent<ItemComponent>();
var itemComp = frame.AddComponent<ItemComponent>();
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<SpriteComponent>(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<StackComponent>(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<ConstructionStepMaterial.MaterialType, string> MaterialPrototypes =
new Dictionary<ConstructionStepMaterial.MaterialType, string>
{
{ 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) private bool TryStartStructureConstruction(IEntity placingEnt, GridCoordinates loc, string prototypeName, Angle angle)
{ {
var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName); var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName);
@@ -84,7 +262,7 @@ namespace Content.Server.GameObjects.EntitySystems
return false; return false;
} }
if (!activeHand.TryGetComponent(out StackComponent stack) || !ConstructionComponent.MaterialStackValidFor(matStep, stack)) if (!activeHand.TryGetComponent(out StackComponent stack) || !MaterialStackValidFor(matStep, stack))
{ {
return false; return false;
} }
@@ -99,14 +277,14 @@ namespace Content.Server.GameObjects.EntitySystems
if (prototype.Stages.Count == 2) if (prototype.Stages.Count == 2)
{ {
// Exactly 2 stages, so don't make an intermediate frame. // 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; ent.Transform.LocalRotation = angle;
} }
else else
{ {
var frame = _serverEntityManager.SpawnEntity("structureconstructionframe", loc); var frame = EntityManager.SpawnEntity("structureconstructionframe", loc);
var construction = frame.GetComponent<ConstructionComponent>(); var construction = frame.GetComponent<ConstructionComponent>();
construction.Init(prototype); SetupComponent(construction, prototype);
frame.Transform.LocalRotation = angle; frame.Transform.LocalRotation = angle;
} }
@@ -136,7 +314,7 @@ namespace Content.Server.GameObjects.EntitySystems
return; return;
} }
if (!activeHand.TryGetComponent(out StackComponent stack) || !ConstructionComponent.MaterialStackValidFor(matStep, stack)) if (!activeHand.TryGetComponent(out StackComponent stack) || !MaterialStackValidFor(matStep, stack))
{ {
return; return;
} }
@@ -151,17 +329,243 @@ namespace Content.Server.GameObjects.EntitySystems
if (prototype.Stages.Count == 2) if (prototype.Stages.Count == 2)
{ {
// Exactly 2 stages, so don't make an intermediate frame. // 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<ItemComponent>()); hands.PutInHandOrDrop(ent.GetComponent<ItemComponent>());
} }
else else
{ {
//TODO: Make these viable as an item and try putting them in the players hands var frame = EntityManager.SpawnEntity("structureconstructionframe", placingEnt.Transform.GridPosition);
var frame = _serverEntityManager.SpawnEntity("structureconstructionframe", placingEnt.Transform.GridPosition);
var construction = frame.GetComponent<ConstructionComponent>(); var construction = frame.GetComponent<ConstructionComponent>();
construction.Init(prototype); SetupComponent(construction, prototype);
var finalPrototype = _prototypeManager.Index<EntityPrototype>(prototype.Result);
if (finalPrototype.Components.TryGetValue("Item", out var itemProtoComp))
{
if(frame.HasComponent<ItemComponent>())
frame.RemoveComponent<ItemComponent>();
var itemComp = frame.AddComponent<ItemComponent>();
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<SpriteComponent>();
var transformComponent = constructEntity.GetComponent<ITransformComponent>();
// 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<IContainerManager>(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<AudioSystem>();
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<ToolComponent>(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<WelderComponent>(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<StackType, ConstructionStepMaterial.MaterialType> StackTypeMap
= new Dictionary<StackType, ConstructionStepMaterial.MaterialType>
{
{ 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<SpriteComponent>();
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<EntityPrototype>(prototype.Result);
frame.Name = $"Unfinished {finalPrototype.Name}";
}
}
/// <summary>
/// A system message that is raised when an entity is trying to be deconstructed.
/// </summary>
public class BeginDeconstructEntityMsg : EntitySystemMessage
{
/// <summary>
/// Entity that initiated the deconstruction.
/// </summary>
public IEntity User { get; }
/// <summary>
/// Tool in the active hand of the user.
/// </summary>
public IEntity Hand { get; }
/// <summary>
/// Target entity that is trying to be deconstructed.
/// </summary>
public IEntity Target { get; }
/// <summary>
/// Set this to true if you would like to block the deconstruction from happening.
/// </summary>
public bool BlockDeconstruct { get; set; }
/// <summary>
/// Constructs a new instance of <see cref="BeginDeconstructEntityMsg"/>.
/// </summary>
/// <param name="user">Entity that initiated the deconstruction.</param>
/// <param name="hand">Tool in the active hand of the user.</param>
/// <param name="target">Target entity that is trying to be deconstructed.</param>
public BeginDeconstructEntityMsg(IEntity user, IEntity hand, IEntity target)
{
User = user;
Hand = hand;
Target = target;
}
}
/// <summary>
/// A component message that is raised when an entity is trying to be deconstructed.
/// </summary>
public class BeginDeconstructCompMsg : ComponentMessage
{
/// <summary>
/// Entity that initiated the deconstruction.
/// </summary>
public IEntity User { get; }
/// <summary>
/// Set this to true if you would like to block the deconstruction from happening.
/// </summary>
public bool BlockDeconstruct { get; set; }
/// <summary>
/// Constructs a new instance of <see cref="BeginDeconstructCompMsg"/>.
/// </summary>
/// <param name="user">Entity that initiated the deconstruction.</param>
public BeginDeconstructCompMsg(IEntity user)
{
User = user;
} }
} }
} }

View File

@@ -48,6 +48,8 @@
steps: steps:
- material: Glass - material: Glass
amount: 2 amount: 2
reverse:
tool: Screwing
- type: construction - type: construction
name: low wall name: low wall

View File

@@ -10,10 +10,16 @@
steps: steps:
- material: Metal - material: Metal
amount: 1 amount: 1
reverse:
tool: Screwing
- material: Cable - material: Cable
amount: 3 amount: 3
reverse:
tool: Screwing
# Should probably use a shard but y'know. # Should probably use a shard but y'know.
- material: Glass - material: Glass
amount: 1 amount: 1
reverse:
tool: Screwing

View File

@@ -82,6 +82,14 @@
bounds: bounds:
all: -0.15,-0.15,0.15,0.15 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 - type: entity
id: HVWireStack id: HVWireStack
name: HV Wire Coil name: HV Wire Coil