614 lines
23 KiB
C#
614 lines
23 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using Content.Shared.Xenoarchaeology.Artifact.Components;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Shared.Xenoarchaeology.Artifact;
|
|
|
|
/// <summary>
|
|
/// User-friendly API for viewing and modifying the complex graph relationship in XenoArtifacts
|
|
/// </summary>
|
|
public abstract partial class SharedXenoArtifactSystem
|
|
{
|
|
/// <summary>
|
|
/// Gets the index, corresponding to a given node, throwing if the node is not present.
|
|
/// </summary>
|
|
public int GetIndex(Entity<XenoArtifactComponent> ent, EntityUid node)
|
|
{
|
|
if (TryGetIndex((ent, ent), node, out var index))
|
|
{
|
|
return index.Value;
|
|
}
|
|
|
|
throw new ArgumentException($"node {ToPrettyString(node)} is not present in {ToPrettyString(ent)}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to get index inside nodes collection, corresponding to a given node EntityUid.
|
|
/// </summary>
|
|
public bool TryGetIndex(Entity<XenoArtifactComponent?> ent, EntityUid node, [NotNullWhen(true)] out int? index)
|
|
{
|
|
index = null;
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return false;
|
|
|
|
for (var i = 0; i < ent.Comp.NodeVertices.Length; i++)
|
|
{
|
|
if (!TryGetNode(ent, i, out var iNode))
|
|
continue;
|
|
|
|
if (node != iNode.Value.Owner)
|
|
continue;
|
|
|
|
index = i;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets node entity with node component from artifact by index of node inside artifact nodes collection.
|
|
/// </summary>
|
|
/// <exception cref="ArgumentException">Throws if requested index doesn't exist on artifact. </exception>
|
|
public Entity<XenoArtifactNodeComponent> GetNode(Entity<XenoArtifactComponent> ent, int index)
|
|
{
|
|
if (ent.Comp.NodeVertices[index] is { } netUid && GetEntity(netUid) is var uid && _nodeQuery.TryComp(uid, out var comp))
|
|
return (uid, comp);
|
|
|
|
throw new ArgumentException($"index {index} does not correspond to an existing node in {ToPrettyString(ent)}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to get node entity with node component from artifact by index of node inside artifact nodes collection.
|
|
/// </summary>
|
|
public bool TryGetNode(Entity<XenoArtifactComponent?> ent, int index, [NotNullWhen(true)] out Entity<XenoArtifactNodeComponent>? node)
|
|
{
|
|
node = null;
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return false;
|
|
|
|
if (index < 0 || index >= ent.Comp.NodeVertices.Length)
|
|
return false;
|
|
|
|
if (ent.Comp.NodeVertices[index] is { } netUid && GetEntity(netUid) is var uid && _nodeQuery.TryComp(uid, out var comp))
|
|
node = (uid, comp);
|
|
|
|
return node != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the index of the first empty spot in the NodeVertices array.
|
|
/// If there is none, resizes both arrays and returns the new index.
|
|
/// </summary>
|
|
public int GetFreeNodeIndex(Entity<XenoArtifactComponent> ent)
|
|
{
|
|
var length = ent.Comp.NodeVertices.Length;
|
|
for (var i = 0; i < length; i++)
|
|
{
|
|
if (ent.Comp.NodeVertices[i] == null)
|
|
return i;
|
|
}
|
|
|
|
ResizeNodeGraph(ent, length + 1);
|
|
return length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracts node entities from artifact container
|
|
/// (uses pre-cached <see cref="XenoArtifactComponent.NodeVertices"/> and mapping from NetEntity).
|
|
/// </summary>
|
|
public IEnumerable<Entity<XenoArtifactNodeComponent>> GetAllNodes(Entity<XenoArtifactComponent> ent)
|
|
{
|
|
foreach (var netNode in ent.Comp.NodeVertices)
|
|
{
|
|
if (TryGetEntity(netNode, out var node) && _nodeQuery.TryComp(node, out var comp))
|
|
yield return (node.Value, comp);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracts enumeration of all indices that artifact node container have.
|
|
/// </summary>
|
|
public IEnumerable<int> GetAllNodeIndices(Entity<XenoArtifactComponent> ent)
|
|
{
|
|
for (var i = 0; i < ent.Comp.NodeVertices.Length; i++)
|
|
{
|
|
if (ent.Comp.NodeVertices[i] is not null)
|
|
yield return i;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds edge between artifact nodes - <see cref="from"/> and <see cref="to"/>
|
|
/// </summary>
|
|
/// <param name="ent">Artifact entity that contains 'from' and 'to' node entities.</param>
|
|
/// <param name="from">Node from which we need to draw edge. </param>
|
|
/// <param name="to">Node to which we need to draw edge. </param>
|
|
/// <param name="dirty">
|
|
/// Marker, if we need to recalculate caches and mark related components dirty to update on client side.
|
|
/// Should be disabled for initial graph creation to not recalculate cache on each node/edge.
|
|
/// </param>
|
|
/// <returns>True if adding edge was successful, false otherwise.</returns>
|
|
public bool AddEdge(Entity<XenoArtifactComponent?> ent, EntityUid from, EntityUid to, bool dirty = true)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return false;
|
|
|
|
if (!TryGetIndex(ent, from, out var fromIdx) ||
|
|
!TryGetIndex(ent, to, out var toIdx))
|
|
return false;
|
|
|
|
return AddEdge(ent, fromIdx.Value, toIdx.Value, dirty: dirty);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds edge between artifact nodes by indices inside node container - <see cref="fromIdx"/> and <see cref="toIdx"/>
|
|
/// </summary>
|
|
/// <param name="ent">Artifact entity that contains 'from' and 'to' node entities.</param>
|
|
/// <param name="fromIdx">Node index inside artifact node container, from which we need to draw edge. </param>
|
|
/// <param name="toIdx">Node index inside artifact node container, to which we need to draw edge. </param>
|
|
/// <param name="dirty">
|
|
/// Marker, if we need to recalculate caches and mark related components dirty to update on client side.
|
|
/// Should be disabled for initial graph creation to not recalculate cache on each node/edge.
|
|
/// </param>
|
|
/// <returns>True if adding edge was successful, false otherwise.</returns>
|
|
public bool AddEdge(Entity<XenoArtifactComponent?> ent, int fromIdx, int toIdx, bool dirty = true)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return false;
|
|
|
|
DebugTools.Assert(fromIdx >= 0 && fromIdx < ent.Comp.NodeVertices.Length, $"fromIdx is out of bounds for fromIdx {fromIdx}");
|
|
DebugTools.Assert(toIdx >= 0 && toIdx < ent.Comp.NodeVertices.Length, $"toIdx is out of bounds for toIdx {toIdx}");
|
|
|
|
if (ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx])
|
|
return false; //Edge already exists
|
|
|
|
// TODO: add a safety check to prohibit cyclic paths.
|
|
|
|
ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx] = true;
|
|
if (dirty)
|
|
{
|
|
RebuildXenoArtifactMetaData(ent);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes edge between artifact nodes.
|
|
/// </summary>
|
|
/// <param name="ent">Artifact entity that contains 'from' and 'to' node entities.</param>
|
|
/// <param name="from">Entity of node from which edge to remove is connected.</param>
|
|
/// <param name="to">Entity of node to which edge to remove is connected.</param>
|
|
/// <param name="dirty">
|
|
/// Marker, if we need to recalculate caches and mark related components dirty to update on client side.
|
|
/// Should be disabled for initial graph creation to not recalculate cache on each node/edge.
|
|
/// </param>
|
|
/// <returns>True if removed edge was successfully, false otherwise.</returns>
|
|
public bool RemoveEdge(Entity<XenoArtifactComponent?> ent, EntityUid from, EntityUid to, bool dirty = true)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return false;
|
|
|
|
if (!TryGetIndex(ent, from, out var fromIdx) ||
|
|
!TryGetIndex(ent, to, out var toIdx))
|
|
return false;
|
|
|
|
return RemoveEdge(ent, fromIdx.Value, toIdx.Value, dirty);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes edge between artifact nodes.
|
|
/// </summary>
|
|
/// <param name="ent">Artifact entity that contains 'from' and 'to' node entities.</param>
|
|
/// <param name="fromIdx"> First node index inside artifact node container, from which we need to remove connecting edge. </param>
|
|
/// <param name="toIdx"> Other node index inside artifact node container, from which we need to remove connecting edge. </param>
|
|
/// <param name="dirty">
|
|
/// Marker, if we need to recalculate caches and mark related components dirty to update on client side.
|
|
/// Should be disabled for initial graph creation to not recalculate cache on each node/edge.
|
|
/// </param>
|
|
/// <returns>True if removed edge was successfully, false otherwise.</returns>
|
|
public bool RemoveEdge(Entity<XenoArtifactComponent?> ent, int fromIdx, int toIdx, bool dirty = true)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return false;
|
|
|
|
DebugTools.Assert(fromIdx >= 0 && fromIdx < ent.Comp.NodeVertices.Length, $"fromIdx is out of bounds for fromIdx {fromIdx}");
|
|
DebugTools.Assert(toIdx >= 0 && toIdx < ent.Comp.NodeVertices.Length, $"toIdx is out of bounds for toIdx {toIdx}");
|
|
|
|
if (!ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx])
|
|
return false; //Edge doesn't exist
|
|
|
|
ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx] = false;
|
|
|
|
if (dirty)
|
|
{
|
|
RebuildXenoArtifactMetaData(ent);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates node entity (spawns) and adds node into artifact node container.
|
|
/// </summary>
|
|
/// <param name="ent">Artifact entity, to container of which node should be added.</param>
|
|
/// <param name="entProtoId">EntProtoId of node to be added.</param>
|
|
/// <param name="node">Created node or null.</param>
|
|
/// <param name="dirty">
|
|
/// Marker, if we need to recalculate caches and mark related components dirty to update on client side.
|
|
/// Should be disabled for initial graph creation to not recalculate cache on each node/edge.
|
|
/// </param>
|
|
/// <returns>True if node creation and adding was successful, false otherwise.</returns>
|
|
public bool AddNode(
|
|
Entity<XenoArtifactComponent?> ent,
|
|
EntProtoId entProtoId,
|
|
[NotNullWhen(true)] out Entity<XenoArtifactNodeComponent>? node,
|
|
bool dirty = true
|
|
)
|
|
{
|
|
node = null;
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return false;
|
|
|
|
var uid = Spawn(entProtoId);
|
|
var comp = EnsureComp<XenoArtifactNodeComponent>(uid);
|
|
node = (uid, comp);
|
|
return AddNode(ent, (node.Value, node.Value.Comp), dirty: dirty);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds node entity to artifact node container.
|
|
/// </summary>
|
|
/// <param name="ent">Artifact entity, to container of which node should be added.</param>
|
|
/// <param name="node">Node entity to add.</param>
|
|
/// <param name="dirty">
|
|
/// Marker, if we need to recalculate caches and mark related components dirty to update on client side.
|
|
/// Should be disabled for initial graph creation to not recalculate cache on each node/edge.
|
|
/// </param>
|
|
/// <returns>True if node adding was successful, false otherwise.</returns>
|
|
public bool AddNode(Entity<XenoArtifactComponent?> ent, Entity<XenoArtifactNodeComponent?> node, bool dirty = true)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp) || !Resolve(node, ref node.Comp, false))
|
|
return false;
|
|
|
|
node.Comp.Attached = ent.Owner;
|
|
|
|
var nodeIdx = GetFreeNodeIndex((ent, ent.Comp));
|
|
_container.Insert(node.Owner, ent.Comp.NodeContainer);
|
|
ent.Comp.NodeVertices[nodeIdx] = GetNetEntity(node);
|
|
|
|
Dirty(node);
|
|
if (dirty)
|
|
{
|
|
RebuildXenoArtifactMetaData(ent);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes artifact node from artifact node container.
|
|
/// </summary>
|
|
/// <param name="ent">Artifact from container of which node should be removed</param>
|
|
/// <param name="node">Node entity to be removed.</param>
|
|
/// <param name="dirty">
|
|
/// Marker, if we need to recalculate caches and mark related components dirty to update on client side.
|
|
/// Should be disabled for initial graph creation to not recalculate cache on each node/edge.
|
|
/// </param>
|
|
/// <returns>True if node was removed successfully, false otherwise.</returns>
|
|
public bool RemoveNode(Entity<XenoArtifactComponent?> ent, Entity<XenoArtifactNodeComponent?> node, bool dirty = true)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp) || !Resolve(node, ref node.Comp, false))
|
|
return false;
|
|
|
|
if (!TryGetIndex(ent, node, out var idx))
|
|
return false; // node isn't attached to this entity.
|
|
|
|
RemoveAllNodeEdges(ent, idx.Value, dirty: false);
|
|
|
|
_container.Remove(node.Owner, ent.Comp.NodeContainer);
|
|
node.Comp.Attached = null;
|
|
ent.Comp.NodeVertices[idx.Value] = null;
|
|
if (dirty)
|
|
{
|
|
RebuildXenoArtifactMetaData(ent);
|
|
}
|
|
|
|
Dirty(node);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove edges, connected to passed artifact node.
|
|
/// </summary>
|
|
/// <param name="ent">Entity of artifact, in node container of which node resides.</param>
|
|
/// <param name="nodeIdx">Index of node (inside node container), for which all edges should be removed.</param>
|
|
/// <param name="dirty">
|
|
/// Marker, if we need to recalculate caches and mark related components dirty to update on client side.
|
|
/// Should be disabled for initial graph creation to not recalculate cache on each node/edge.
|
|
/// </param>
|
|
public void RemoveAllNodeEdges(Entity<XenoArtifactComponent?> ent, int nodeIdx, bool dirty = true)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return;
|
|
|
|
var predecessors = GetDirectPredecessorNodes(ent, nodeIdx);
|
|
foreach (var p in predecessors)
|
|
{
|
|
RemoveEdge(ent, p, nodeIdx, dirty: false);
|
|
}
|
|
|
|
var successors = GetDirectSuccessorNodes(ent, nodeIdx);
|
|
foreach (var s in successors)
|
|
{
|
|
RemoveEdge(ent, nodeIdx, s, dirty: false);
|
|
}
|
|
|
|
if (dirty)
|
|
{
|
|
RebuildXenoArtifactMetaData(ent);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets set of node entities, that are direct predecessors to passed node entity.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Direct predecessors are nodes, which are connected by edges directly to target node,
|
|
/// and are on outgoing ('FROM') side of edge connection.
|
|
/// </remarks>
|
|
public HashSet<Entity<XenoArtifactNodeComponent>> GetDirectPredecessorNodes(Entity<XenoArtifactComponent?> ent, EntityUid node)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return new();
|
|
|
|
if (!TryGetIndex(ent, node, out var index))
|
|
return new();
|
|
|
|
var indices = GetDirectPredecessorNodes(ent, index.Value);
|
|
var output = new HashSet<Entity<XenoArtifactNodeComponent>>();
|
|
foreach (var i in indices)
|
|
{
|
|
if (TryGetNode(ent, i, out var predecessor))
|
|
output.Add(predecessor.Value);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets set of node indices (in artifact node container) which are direct predecessors to node with passed node index.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Direct predecessors are nodes, which are connected by edges directly to target node,
|
|
/// and are on outgoing ('FROM') side of edge connection.
|
|
/// </remarks>
|
|
public HashSet<int> GetDirectPredecessorNodes(Entity<XenoArtifactComponent?> ent, int nodeIdx)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return new();
|
|
|
|
DebugTools.Assert(nodeIdx >= 0 && nodeIdx < ent.Comp.NodeVertices.Length, $"node index {nodeIdx} is out of bounds!");
|
|
|
|
var indices = new HashSet<int>();
|
|
for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixRows; i++)
|
|
{
|
|
if (ent.Comp.NodeAdjacencyMatrix[i][nodeIdx])
|
|
indices.Add(i);
|
|
}
|
|
|
|
return indices;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets set of node entities, that are direct successors to passed node entity.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Direct successors are nodes, which are connected by edges
|
|
/// directly to target node, and are on incoming ('TO') side of edge connection.
|
|
/// </remarks>
|
|
public HashSet<Entity<XenoArtifactNodeComponent>> GetDirectSuccessorNodes(Entity<XenoArtifactComponent?> ent, EntityUid node)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return new();
|
|
|
|
if (!TryGetIndex(ent, node, out var index))
|
|
return new();
|
|
|
|
var indices = GetDirectSuccessorNodes(ent, index.Value);
|
|
var output = new HashSet<Entity<XenoArtifactNodeComponent>>();
|
|
foreach (var i in indices)
|
|
{
|
|
if (TryGetNode(ent, i, out var successor))
|
|
output.Add(successor.Value);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets set of node indices (in artifact node container) which are direct successors to node with passed node index.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Direct successors are nodes, which are connected by edges
|
|
/// directly to target node, and are on incoming ('TO') side of edge connection.
|
|
/// </remarks>
|
|
public HashSet<int> GetDirectSuccessorNodes(Entity<XenoArtifactComponent?> ent, int nodeIdx)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return new();
|
|
DebugTools.Assert(nodeIdx >= 0 && nodeIdx < ent.Comp.NodeVertices.Length, "node index is out of bounds!");
|
|
|
|
var indices = new HashSet<int>();
|
|
for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixColumns; i++)
|
|
{
|
|
if (ent.Comp.NodeAdjacencyMatrix[nodeIdx][i])
|
|
indices.Add(i);
|
|
}
|
|
|
|
return indices;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets set of node entities, that are predecessors to passed node entity.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Predecessors are nodes, which are connected by edges directly to target node on 'FROM' side of edge,
|
|
/// or connected to such node on 'FROM' side of edge, etc recursively.
|
|
/// </remarks>
|
|
public HashSet<Entity<XenoArtifactNodeComponent>> GetPredecessorNodes(Entity<XenoArtifactComponent?> ent, Entity<XenoArtifactNodeComponent> node)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return new();
|
|
|
|
var predecessors = GetPredecessorNodes(ent, GetIndex((ent, ent.Comp), node));
|
|
var output = new HashSet<Entity<XenoArtifactNodeComponent>>();
|
|
foreach (var p in predecessors)
|
|
{
|
|
output.Add(GetNode((ent, ent.Comp), p));
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets set of node indices inside artifact node container, that are predecessors to entity with passed node index.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Predecessors are nodes, which are connected by edges directly to target node on 'FROM' side of edge,
|
|
/// or connected to such node on 'FROM' side of edge, etc recursively.
|
|
/// </remarks>
|
|
public HashSet<int> GetPredecessorNodes(Entity<XenoArtifactComponent?> ent, int nodeIdx)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return new();
|
|
|
|
var predecessors = GetDirectPredecessorNodes(ent, nodeIdx);
|
|
if (predecessors.Count == 0)
|
|
return new();
|
|
|
|
var output = new HashSet<int>();
|
|
foreach (var p in predecessors)
|
|
{
|
|
output.Add(p);
|
|
var recursivePredecessors = GetPredecessorNodes(ent, p);
|
|
foreach (var rp in recursivePredecessors)
|
|
{
|
|
output.Add(rp);
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets set of node entities, that are successors to passed node entity.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Successors are nodes, which are connected by edges directly to target node on 'TO' side of edge,
|
|
/// or connected to such node on 'TO' side of edge, etc recursively.
|
|
/// </remarks>
|
|
public HashSet<Entity<XenoArtifactNodeComponent>> GetSuccessorNodes(Entity<XenoArtifactComponent?> ent, Entity<XenoArtifactNodeComponent> node)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return new();
|
|
|
|
var successors = GetSuccessorNodes(ent, GetIndex((ent, ent.Comp), node));
|
|
var output = new HashSet<Entity<XenoArtifactNodeComponent>>();
|
|
foreach (var s in successors)
|
|
{
|
|
output.Add(GetNode((ent, ent.Comp), s));
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets set of node indices inside artifact node container, that are successors to entity with passed node index.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Successors are nodes, which are connected by edges directly to target node on 'TO' side of edge,
|
|
/// or connected to such node on 'TO' side of edge, etc recursively.
|
|
/// </remarks>
|
|
public HashSet<int> GetSuccessorNodes(Entity<XenoArtifactComponent?> ent, int nodeIdx)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return new();
|
|
|
|
var successors = GetDirectSuccessorNodes(ent, nodeIdx);
|
|
if (successors.Count == 0)
|
|
return new();
|
|
|
|
var output = new HashSet<int>();
|
|
foreach (var s in successors)
|
|
{
|
|
output.Add(s);
|
|
var recursiveSuccessors = GetSuccessorNodes(ent, s);
|
|
foreach (var rs in recursiveSuccessors)
|
|
{
|
|
output.Add(rs);
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines, if there is an edge (directed link) FROM one node TO other in passed artifact.
|
|
/// </summary>
|
|
/// <param name="ent">Artifact, inside which node container nodes are.</param>
|
|
/// <param name="from">Node FROM which existence of edge should be checked.</param>
|
|
/// <param name="to">Node TO which existance of edge should be checked.</param>
|
|
public bool NodeHasEdge(
|
|
Entity<XenoArtifactComponent?> ent,
|
|
Entity<XenoArtifactNodeComponent?> from,
|
|
Entity<XenoArtifactNodeComponent?> to
|
|
)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return new();
|
|
|
|
var fromIdx = GetIndex((ent, ent.Comp), from);
|
|
var toIdx = GetIndex((ent, ent.Comp), to);
|
|
|
|
return ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resizes the adjacency matrix and vertices array to <paramref name="newSize"/>,
|
|
/// or at least what it WOULD do if i wasn't forced to use shitty lists.
|
|
/// </summary>
|
|
protected void ResizeNodeGraph(Entity<XenoArtifactComponent> ent, int newSize)
|
|
{
|
|
Array.Resize(ref ent.Comp.NodeVertices, newSize);
|
|
|
|
while (ent.Comp.NodeAdjacencyMatrix.Count < newSize)
|
|
{
|
|
ent.Comp.NodeAdjacencyMatrix.Add(new());
|
|
}
|
|
|
|
foreach (var row in ent.Comp.NodeAdjacencyMatrix)
|
|
{
|
|
while (row.Count < newSize)
|
|
{
|
|
row.Add(false);
|
|
}
|
|
}
|
|
|
|
Dirty(ent);
|
|
}
|
|
|
|
/// <summary> Removes unlocking state from artifact. </summary>
|
|
private void CancelUnlockingOnGraphStructureChange(Entity<XenoArtifactComponent> ent)
|
|
{
|
|
if (!TryComp<XenoArtifactUnlockingComponent>(ent, out var unlockingComponent))
|
|
return;
|
|
|
|
Entity<XenoArtifactUnlockingComponent, XenoArtifactComponent> artifactEnt = (ent, unlockingComponent, ent.Comp);
|
|
CancelUnlockingState(artifactEnt);
|
|
}
|
|
}
|