Documents more ConstructionSystem methods. (#7246)

This commit is contained in:
Vera Aguilera Puerto
2022-03-25 05:00:39 +01:00
committed by GitHub
parent b8ab62b2c6
commit 380bb89f83
4 changed files with 201 additions and 9 deletions

View File

@@ -18,6 +18,15 @@ namespace Content.Server.Construction
{
}
/// <summary>
/// Sets a container on an entity as being handled by Construction. This essentially means that it will
/// be transferred if the entity prototype changes. <seealso cref="ChangeEntity"/>
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="container">The container identifier. This method does not check whether the container exists.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>Whether we could set the container as being handled by construction or not. Also returns false if
/// the entity does not have a <see cref="ConstructionComponent"/>.</returns>
public bool AddContainer(EntityUid uid, string container, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction))
@@ -26,14 +35,32 @@ namespace Content.Server.Construction
return construction.Containers.Add(container);
}
/// <summary>
/// Gets the current construction graph of an entity, or null.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>The current construction graph of an entity or null if invalid. Also returns null if the entity
/// does not have a <see cref="ConstructionComponent"/>.</returns>
/// <remarks>An entity with a valid construction state will always have a valid graph.</remarks>
public ConstructionGraphPrototype? GetCurrentGraph(EntityUid uid, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction, false))
return null;
// If the set graph prototype does not exist, also return null. This could be due to admemes changing values
// in ViewVariables, so even though the construction state is invalid, just return null.
return _prototypeManager.TryIndex(construction.Graph, out ConstructionGraphPrototype? graph) ? graph : null;
}
/// <summary>
/// Gets the construction graph node the entity is currently at, or null.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>The current construction graph node the entity is currently at, or null if invalid. Also returns
/// null if the entity does not have a <see cref="ConstructionComponent"/>.</returns>
/// <remarks>An entity with a valid construction state will always be at a valid node.</remarks>
public ConstructionGraphNode? GetCurrentNode(EntityUid uid, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction, false))
@@ -45,6 +72,14 @@ namespace Content.Server.Construction
return GetCurrentGraph(uid, construction) is not {} graph ? null : GetNodeFromGraph(graph, nodeIdentifier);
}
/// <summary>
/// Gets the construction graph edge the entity is currently at, or null.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>The construction graph edge the entity is currently at, if any. Also returns null if the entity
/// does not have a <see cref="ConstructionComponent"/>.</returns>
/// <remarks>An entity with a valid construction state might not always be at an edge.</remarks>
public ConstructionGraphEdge? GetCurrentEdge(EntityUid uid, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction, false))
@@ -56,6 +91,14 @@ namespace Content.Server.Construction
return GetCurrentNode(uid, construction) is not {} node ? null : GetEdgeFromNode(node, edgeIndex);
}
/// <summary>
/// Gets the construction graph step the entity is currently at, or null.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>The construction graph step the entity is currently at, if any. Also returns null if the entity
/// does not have a <see cref="ConstructionComponent"/>.</returns>
/// <remarks>An entity with a valid construction state might not always be at a step or an edge.</remarks>
public ConstructionGraphStep? GetCurrentStep(EntityUid uid, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction, false))
@@ -67,6 +110,15 @@ namespace Content.Server.Construction
return GetStepFromEdge(edge, construction.StepIndex);
}
/// <summary>
/// Gets the construction graph node the entity's construction pathfinding is currently targeting, if any.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>The construction graph node the entity's construction pathfinding is currently targeting, or null
/// if it's not currently targeting any node. Also returns null if the entity does not have a
/// <see cref="ConstructionComponent"/>.</returns>
/// <remarks>Target nodes are entirely optional and only used for pathfinding purposes.</remarks>
public ConstructionGraphNode? GetTargetNode(EntityUid uid, ConstructionComponent? construction)
{
if (!Resolve(uid, ref construction))
@@ -81,6 +133,16 @@ namespace Content.Server.Construction
return GetNodeFromGraph(graph, targetNodeId);
}
/// <summary>
/// Gets the construction graph edge the entity's construction pathfinding is currently targeting, if any.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>The construction graph edge the entity's construction pathfinding is currently targeting, or null
/// if it's not currently targeting any edge. Also returns null if the entity does not have a
/// <see cref="ConstructionComponent"/>.</returns>
/// <remarks>Target edges are entirely optional and only used for pathfinding purposes. The targeted edge will
/// be an edge on the current construction node the entity is at.</remarks>
public ConstructionGraphEdge? GetTargetEdge(EntityUid uid, ConstructionComponent? construction)
{
if (!Resolve(uid, ref construction))
@@ -95,6 +157,13 @@ namespace Content.Server.Construction
return GetEdgeFromNode(node, targetEdgeIndex);
}
/// <summary>
/// Gets both the construction edge and step the entity is currently at, if any.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>A tuple containing the current edge and step the entity's construction state is at.</returns>
/// <remarks>The edge, step or both could be null. A valid construction state does not necessarily need them.</remarks>
public (ConstructionGraphEdge? edge, ConstructionGraphStep? step) GetCurrentEdgeAndStep(EntityUid uid,
ConstructionComponent? construction = null)
{
@@ -111,21 +180,49 @@ namespace Content.Server.Construction
return (edge, step);
}
/// <summary>
/// Gets a node from a construction graph given its identifier.
/// </summary>
/// <param name="graph">The construction graph where to get the node.</param>
/// <param name="id">The identifier that corresponds to the node.</param>
/// <returns>The node that corresponds to the identifier, or null if it doesn't exist.</returns>
public ConstructionGraphNode? GetNodeFromGraph(ConstructionGraphPrototype graph, string id)
{
return graph.Nodes.TryGetValue(id, out var node) ? node : null;
}
/// <summary>
/// Gets an edge from a construction node given its index.
/// </summary>
/// <param name="node">The construction node where to get the edge.</param>
/// <param name="index">The index or position of the edge on the node.</param>
/// <returns>The edge on that index in the construction node, or null if none.</returns>
public ConstructionGraphEdge? GetEdgeFromNode(ConstructionGraphNode node, int index)
{
return node.Edges.Count > index ? node.Edges[index] : null;
}
/// <summary>
/// Gets a step from a construction edge given its index.
/// </summary>
/// <param name="edge">The construction edge where to get the step.</param>
/// <param name="index">The index or position of the step on the edge.</param>
/// <returns>The edge on that index in the construction edge, or null if none.</returns>
public ConstructionGraphStep? GetStepFromEdge(ConstructionGraphEdge edge, int index)
{
return edge.Steps.Count > index ? edge.Steps[index] : null;
}
/// <summary>
/// Performs a node change on a construction entity, optionally performing the actions for the new node.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="userUid">An optional user entity, for actions.</param>
/// <param name="id">The identifier of the node to change to.</param>
/// <param name="performActions">Whether the actions for the new node will be performed or not.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>Whether the node change succeeded or not. Also returns false if the entity does not have a <see cref="ConstructionComponent"/>.</returns>
/// <remarks>This method also updates the construction pathfinding automatically, if the node change succeeds.</remarks>
public bool ChangeNode(EntityUid uid, EntityUid? userUid, string id, bool performActions = true, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction))
@@ -152,6 +249,20 @@ namespace Content.Server.Construction
return true;
}
/// <summary>
/// Performs an entity prototype change on a construction entity.
/// The old entity will be removed, and a new one will be spawned in its place. Some values will be kept,
/// and any containers handled by construction will be transferred to the new entity as well.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="userUid">An optional user entity, for actions.</param>
/// <param name="newEntity">The entity prototype identifier for the new entity.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <param name="metaData">The metadata component of the target entity. Will be resolved if null.</param>
/// <param name="transform">The transform component of the target entity. Will be resolved if null.</param>
/// <param name="containerManager">The container manager component of the target entity. Will be resolved if null,
/// but it is an optional component and not required for the method to work.</param>
/// <returns>The new entity, or null if the method did not succeed.</returns>
private EntityUid? ChangeEntity(EntityUid uid, EntityUid? userUid, string newEntity,
ConstructionComponent? construction = null,
MetaDataComponent? metaData = null,
@@ -189,7 +300,7 @@ namespace Content.Server.Construction
}
// Transform transferring.
var newTransform = EntityManager.GetComponent<TransformComponent>(newUid);
var newTransform = Transform(newUid);
newTransform.LocalRotation = transform.LocalRotation;
newTransform.Anchored = transform.Anchored;
@@ -217,7 +328,7 @@ namespace Content.Server.Construction
}
}
EntityManager.QueueDeleteEntity(uid);
QueueDel(uid);
if(GetCurrentNode(newUid, newConstruction) is {} node)
PerformActions(newUid, userUid, node.Actions);
@@ -225,6 +336,18 @@ namespace Content.Server.Construction
return newUid;
}
/// <summary>
/// Performs a construction graph change on a construction entity, also changing the node to a valid one on
/// the new graph.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="userUid">An optional user entity, for actions.</param>
/// <param name="graphId">The identifier for the construction graph to switch to.</param>
/// <param name="nodeId">The identifier for a node on the new construction graph to switch to.</param>
/// <param name="performActions">Whether actions on the new node will be performed or not.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>Whether the construction graph change succeeded or not. Returns false if the entity does not have
/// a <see cref="ConstructionComponent"/>.</returns>
public bool ChangeGraph(EntityUid uid, EntityUid? userUid, string graphId, string nodeId, bool performActions = true, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction))
@@ -233,7 +356,7 @@ namespace Content.Server.Construction
if (!_prototypeManager.TryIndex<ConstructionGraphPrototype>(graphId, out var graph))
return false;
if(GetNodeFromGraph(graph, nodeId) is not {} node)
if(GetNodeFromGraph(graph, nodeId) is not {})
return false;
construction.Graph = graphId;

View File

@@ -102,6 +102,14 @@ namespace Content.Server.Construction
}
/// <summary>
/// Returns a <see cref="ConstructionGuide"/> for a given <see cref="ConstructionPrototype"/>,
/// generating and caching it as needed.
/// </summary>
/// <param name="construction">The construction prototype to generate the guide for. We must be able to pathfind
/// from its starting node to its ending node to be able to generate a guide for it.</param>
/// <returns>The guide for the given construction, or null if we can't pathfind from the start node to the
/// end node on that construction.</returns>
private ConstructionGuide? GetGuide(ConstructionPrototype construction)
{
// NOTE: This method might be allocate a fair bit, but do not worry!

View File

@@ -312,7 +312,7 @@ namespace Content.Server.Construction
// we split the stack in two and insert the split stack.
if (insertStep is MaterialConstructionGraphStep materialInsertStep)
{
if (_stackSystem.Split(insert, materialInsertStep.Amount, EntityManager.GetComponent<TransformComponent>(interactUsing.User).Coordinates) is not {} stack)
if (_stackSystem.Split(insert, materialInsertStep.Amount, Transform(interactUsing.User).Coordinates) is not {} stack)
return HandleResult.False;
insert = stack;
@@ -334,7 +334,7 @@ namespace Content.Server.Construction
else
{
// If we don't store the item in a container on the entity, we just delete it right away.
EntityManager.DeleteEntity(insert);
Del(insert);
}
// Step has been handled correctly, so we signal this.
@@ -387,6 +387,13 @@ namespace Content.Server.Construction
return HandleResult.False;
}
/// <summary>
/// Checks whether a number of <see cref="IGraphCondition"/>s are true for a given entity.
/// </summary>
/// <param name="uid">The entity to pass to the conditions.</param>
/// <param name="conditions">The conditions to evaluate.</param>
/// <remarks>This method is short-circuiting; if a condition evaluates to false, we stop checking the rest of conditions.</remarks>
/// <returns>Whether all conditions evaluate to true for the given entity.</returns>
public bool CheckConditions(EntityUid uid, IEnumerable<IGraphCondition> conditions)
{
foreach (var condition in conditions)
@@ -398,11 +405,19 @@ namespace Content.Server.Construction
return true;
}
/// <summary>
/// Performs a number of <see cref="IGraphAction"/>s for a given entity, with an optional user entity.
/// </summary>
/// <param name="uid">The entity to perform the actions on.</param>
/// <param name="userUid">An optional user entity to pass into the actions.</param>
/// <param name="actions">The actions to perform.</param>
/// <remarks>This method checks whether the given target entity exists before performing any actions.
/// If the entity is deleted by an action, it will short-circuit and stop performing the rest of actions.</remarks>
public void PerformActions(EntityUid uid, EntityUid? userUid, IEnumerable<IGraphAction> actions)
{
foreach (var action in actions)
{
// If an action deletes the entity, we stop performing actions.
// If an action deletes the entity, we stop performing the rest of actions.
if (!Exists(uid))
break;
@@ -410,6 +425,12 @@ namespace Content.Server.Construction
}
}
/// <summary>
/// Resets the current construction edge status on an entity.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="construction">The construction component. If null, it will be resolved on the entity.</param>
/// <remarks>This method updates the construction pathfinding on the entity automatically.</remarks>
public void ResetEdge(EntityUid uid, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction))
@@ -419,6 +440,7 @@ namespace Content.Server.Construction
construction.EdgeIndex = null;
construction.StepIndex = 0;
// Update pathfinding to keep it in sync with the current construction status.
UpdatePathfinding(uid, construction);
}
@@ -430,7 +452,7 @@ namespace Content.Server.Construction
foreach (var uid in _constructionUpdateQueue)
{
// Ensure the entity exists and has a Construction component.
if (!EntityManager.EntityExists(uid) || !EntityManager.TryGetComponent(uid, out ConstructionComponent? construction))
if (!Exists(uid) || !TryComp(uid, out ConstructionComponent? construction))
continue;
// Handle all queued interactions!
@@ -446,6 +468,16 @@ namespace Content.Server.Construction
#region Event Handlers
/// <summary>
/// Queues a directed event to be handled by construction on the next update tick.
/// Used as a handler for any events that construction can listen to. <seealso cref="InitializeInteractions"/>
/// </summary>
/// <param name="uid">The entity the event is directed to.</param>
/// <param name="construction">The construction component to queue the event on.</param>
/// <param name="args">The directed event to be queued.</param>
/// <remarks>Events inheriting <see cref="HandledEntityEventArgs"/> are treated specially by this method.
/// They will only be queued if they can be validated against the current construction state,
/// in which case they will also be set as handled.</remarks>
private void EnqueueEvent(EntityUid uid, ConstructionComponent construction, object args)
{
// Handled events get treated specially.
@@ -473,7 +505,7 @@ namespace Content.Server.Construction
private void OnDoAfterComplete(ConstructionDoAfterComplete ev)
{
// Make extra sure the target entity exists...
if (!EntityManager.EntityExists(ev.TargetUid))
if (!Exists(ev.TargetUid))
return;
// Re-raise this event, but directed on the target UID.
@@ -483,7 +515,7 @@ namespace Content.Server.Construction
private void OnDoAfterCancelled(ConstructionDoAfterCancelled ev)
{
// Make extra sure the target entity exists...
if (!EntityManager.EntityExists(ev.TargetUid))
if (!Exists(ev.TargetUid))
return;
// Re-raise this event, but directed on the target UID.

View File

@@ -8,6 +8,13 @@ namespace Content.Server.Construction
{
public sealed partial class ConstructionSystem
{
/// <summary>
/// Sets or clears a pathfinding target node for a given construction entity.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="targetNodeId">The target node to pathfind, or null to clear the current pathfinding node.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>Whether we could set/clear the pathfinding target node.</returns>
public bool SetPathfindingTarget(EntityUid uid, string? targetNodeId, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction))
@@ -34,6 +41,12 @@ namespace Content.Server.Construction
return UpdatePathfinding(uid, graph, node, targetNode, GetCurrentEdge(uid, construction), construction);
}
/// <summary>
/// Updates the pathfinding state for the current construction state of an entity.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>Whether we could update the pathfinding state correctly.</returns>
public bool UpdatePathfinding(EntityUid uid, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction))
@@ -50,6 +63,17 @@ namespace Content.Server.Construction
return UpdatePathfinding(uid, graph, node, targetNode, GetCurrentEdge(uid, construction), construction);
}
/// <summary>
/// Internal version of <see cref="UpdatePathfinding"/>, which expects a valid construction state and
/// actually performs the pathfinding update logic.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="graph">The construction graph the entity is at.</param>
/// <param name="currentNode">The current construction node the entity is at.</param>
/// <param name="targetNode">The target node we are trying to reach on the graph.</param>
/// <param name="currentEdge">The current edge the entity is at, or null if none.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
/// <returns>Whether we could update the pathfinding state correctly.</returns>
private bool UpdatePathfinding(EntityUid uid, ConstructionGraphPrototype graph,
ConstructionGraphNode currentNode, ConstructionGraphNode targetNode,
ConstructionGraphEdge? currentEdge,
@@ -111,6 +135,11 @@ namespace Content.Server.Construction
return true;
}
/// <summary>
/// Clears the pathfinding targets on a construction entity.
/// </summary>
/// <param name="uid">The target entity.</param>
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
public void ClearPathfinding(EntityUid uid, ConstructionComponent? construction = null)
{
if (!Resolve(uid, ref construction))