Add "Begin Deconstruction" verb, unfinished particle accelerator bits will tell you how to finish them (#2706)

* More Construction Pieces: Default construction targets

* More Construction Pieces: Unfinished particle accelerator will tell you how to finish it

* More Construction Pieces: Construction system actually pathfinds any-to-any

* More Construction Pieces: Verb to begin deconstruction of an object
This commit is contained in:
20kdc
2020-12-08 10:53:37 +00:00
committed by GitHub
parent f6d62ada65
commit d75c22d5e1
5 changed files with 89 additions and 7 deletions

View File

@@ -0,0 +1,52 @@
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Log;
namespace Content.Server.GameObjects.Components.Construction
{
public partial class ConstructionComponent
{
[Verb]
public sealed class DeconstructibleVerb : Verb<ConstructionComponent>
{
protected override void GetData(IEntity user, ConstructionComponent component, VerbData data)
{
if (!ActionBlockerSystem.CanInteract(user))
{
data.Visibility = VerbVisibility.Invisible;
return;
}
if (((component.Target != null) && (component.Target.Name == component.DeconstructionNodeIdentifier)) ||
((component.Node != null) && (component.Node.Name == component.DeconstructionNodeIdentifier)))
{
data.Visibility = VerbVisibility.Invisible;
return;
}
data.CategoryData = VerbCategories.Construction;
data.Text = Loc.GetString("Begin deconstructing");
data.IconTexture = "/Textures/Interface/VerbIcons/rotate_ccw.svg.96dpi.png";
}
protected override void Activate(IEntity user, ConstructionComponent component)
{
component.SetNewTarget(component.DeconstructionNodeIdentifier);
if (component.Target == null)
{
// Maybe check, but on the flip-side a better solution might be to not make it undeconstructible in the first place, no?
component.Owner.PopupMessage(user, Loc.GetString("There is no way to deconstruct this."));
}
else
{
component.Owner.PopupMessage(user, Loc.GetString("Examine to see instructions."));
}
}
}
}
}

View File

@@ -28,7 +28,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Construction namespace Content.Server.GameObjects.Components.Construction
{ {
[RegisterComponent] [RegisterComponent]
public class ConstructionComponent : Component, IExamine, IInteractUsing public partial class ConstructionComponent : Component, IExamine, IInteractUsing
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
@@ -39,6 +39,7 @@ namespace Content.Server.GameObjects.Components.Construction
private TaskCompletionSource<object>? _handlingTask = null; private TaskCompletionSource<object>? _handlingTask = null;
private string _graphIdentifier = string.Empty; private string _graphIdentifier = string.Empty;
private string _startingNodeIdentifier = string.Empty; private string _startingNodeIdentifier = string.Empty;
private string _startingTargetNodeIdentifier = string.Empty;
[ViewVariables] [ViewVariables]
private HashSet<string> _containers = new(); private HashSet<string> _containers = new();
@@ -79,6 +80,9 @@ namespace Content.Server.GameObjects.Components.Construction
[ViewVariables] [ViewVariables]
public int EdgeStep { get; private set; } = 0; public int EdgeStep { get; private set; } = 0;
[ViewVariables]
public string DeconstructionNodeIdentifier { get; private set; } = "start";
/// <inheritdoc /> /// <inheritdoc />
public override void ExposeData(ObjectSerializer serializer) public override void ExposeData(ObjectSerializer serializer)
{ {
@@ -86,6 +90,8 @@ namespace Content.Server.GameObjects.Components.Construction
serializer.DataField(ref _graphIdentifier, "graph", string.Empty); serializer.DataField(ref _graphIdentifier, "graph", string.Empty);
serializer.DataField(ref _startingNodeIdentifier, "node", string.Empty); serializer.DataField(ref _startingNodeIdentifier, "node", string.Empty);
serializer.DataField(ref _startingTargetNodeIdentifier, "defaultTarget", string.Empty);
serializer.DataField(this, x => x.DeconstructionNodeIdentifier, "deconstructionTarget", "start");
} }
/// <summary> /// <summary>
@@ -495,6 +501,9 @@ namespace Content.Server.GameObjects.Components.Construction
{ {
Logger.Error($"Couldn't find prototype {_graphIdentifier} in construction component!"); Logger.Error($"Couldn't find prototype {_graphIdentifier} in construction component!");
} }
if (!string.IsNullOrEmpty(_startingTargetNodeIdentifier))
SetNewTarget(_startingTargetNodeIdentifier);
} }
protected override void Startup() protected override void Startup()

View File

@@ -14,7 +14,7 @@ namespace Content.Shared.Construction
{ {
private readonly Dictionary<string, ConstructionGraphNode> _nodes = new(); private readonly Dictionary<string, ConstructionGraphNode> _nodes = new();
private readonly Dictionary<ValueTuple<string, string>, ConstructionGraphNode[]> _paths = new(); private readonly Dictionary<ValueTuple<string, string>, ConstructionGraphNode[]> _paths = new();
private Dictionary<ConstructionGraphNode, ConstructionGraphNode> _pathfinding = new(); private readonly Dictionary<string, Dictionary<ConstructionGraphNode, ConstructionGraphNode>> _pathfinding = new();
[ViewVariables] [ViewVariables]
public string ID { get; private set; } public string ID { get; private set; }
@@ -44,8 +44,6 @@ namespace Content.Shared.Construction
if(string.IsNullOrEmpty(Start) || !_nodes.ContainsKey(Start)) if(string.IsNullOrEmpty(Start) || !_nodes.ContainsKey(Start))
throw new InvalidDataException($"Starting node for construction graph {ID} is null, empty or invalid!"); throw new InvalidDataException($"Starting node for construction graph {ID} is null, empty or invalid!");
_pathfinding = Pathfind(Start);
} }
public ConstructionGraphEdge Edge(string startNode, string nextNode) public ConstructionGraphEdge Edge(string startNode, string nextNode)
@@ -61,6 +59,20 @@ namespace Content.Shared.Construction
if (_paths.ContainsKey(tuple)) if (_paths.ContainsKey(tuple))
return _paths[tuple]; return _paths[tuple];
// Get graph given the current start.
Dictionary<ConstructionGraphNode, ConstructionGraphNode> pathfindingForStart;
if (_pathfinding.ContainsKey(startNode))
{
pathfindingForStart = _pathfinding[startNode];
}
else
{
pathfindingForStart = _pathfinding[startNode] = PathsForStart(startNode);
}
// Follow the chain backwards.
var start = _nodes[startNode]; var start = _nodes[startNode];
var finish = _nodes[finishNode]; var finish = _nodes[finishNode];
@@ -71,14 +83,14 @@ namespace Content.Shared.Construction
path.Add(current); path.Add(current);
// No path. // No path.
if (current == null || !_pathfinding.ContainsKey(current)) if (current == null || !pathfindingForStart.ContainsKey(current))
{ {
// We remember this for next time. // We remember this for next time.
_paths[tuple] = null; _paths[tuple] = null;
return null; return null;
} }
current = _pathfinding[current]; current = pathfindingForStart[current];
} }
path.Reverse(); path.Reverse();
@@ -89,7 +101,7 @@ namespace Content.Shared.Construction
/// Uses breadth first search for pathfinding. /// Uses breadth first search for pathfinding.
/// </summary> /// </summary>
/// <param name="start"></param> /// <param name="start"></param>
private Dictionary<ConstructionGraphNode, ConstructionGraphNode> Pathfind(string start) private Dictionary<ConstructionGraphNode, ConstructionGraphNode> PathsForStart(string start)
{ {
// TODO: Make this use A* or something, although it's not that important. // TODO: Make this use A* or something, although it's not that important.
var startNode = _nodes[start]; var startNode = _nodes[start];

View File

@@ -9,5 +9,6 @@ namespace Content.Shared.GameObjects.Verbs
("Debug", "/Textures/Interface/VerbIcons/debug.svg.96dpi.png"); ("Debug", "/Textures/Interface/VerbIcons/debug.svg.96dpi.png");
public static readonly VerbCategoryData Rotate = ("Rotate", null); public static readonly VerbCategoryData Rotate = ("Rotate", null);
public static readonly VerbCategoryData Construction = ("Construction", null);
} }
} }

View File

@@ -250,6 +250,7 @@
- type: Construction - type: Construction
graph: particleAcceleratorControlBox graph: particleAcceleratorControlBox
node: start node: start
defaultTarget: completed
- type: entity - type: entity
parent: ParticleAcceleratorBase parent: ParticleAcceleratorBase
@@ -266,6 +267,7 @@
- type: Construction - type: Construction
graph: particleAcceleratorEmitterLeft graph: particleAcceleratorEmitterLeft
node: start node: start
defaultTarget: completed
- type: entity - type: entity
parent: ParticleAcceleratorBase parent: ParticleAcceleratorBase
@@ -282,6 +284,7 @@
- type: Construction - type: Construction
graph: particleAcceleratorEmitterCenter graph: particleAcceleratorEmitterCenter
node: start node: start
defaultTarget: completed
- type: entity - type: entity
parent: ParticleAcceleratorBase parent: ParticleAcceleratorBase
@@ -298,6 +301,7 @@
- type: Construction - type: Construction
graph: particleAcceleratorEmitterRight graph: particleAcceleratorEmitterRight
node: start node: start
defaultTarget: completed
- type: entity - type: entity
parent: ParticleAcceleratorBase parent: ParticleAcceleratorBase
@@ -314,6 +318,7 @@
- type: Construction - type: Construction
graph: particleAcceleratorEndCap graph: particleAcceleratorEndCap
node: start node: start
defaultTarget: completed
- type: entity - type: entity
parent: ParticleAcceleratorBase parent: ParticleAcceleratorBase
@@ -330,6 +335,7 @@
- type: Construction - type: Construction
graph: particleAcceleratorFuelChamber graph: particleAcceleratorFuelChamber
node: start node: start
defaultTarget: completed
- type: entity - type: entity
parent: ParticleAcceleratorBase parent: ParticleAcceleratorBase
@@ -346,3 +352,5 @@
- type: Construction - type: Construction
graph: particleAcceleratorPowerBox graph: particleAcceleratorPowerBox
node: start node: start
defaultTarget: completed